rmultimarkdown 6.2.2.1 → 6.4.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/ext/Makefile +2 -2
  3. data/ext/mmd/aho-corasick.c +12 -8
  4. data/ext/mmd/beamer.c +29 -0
  5. data/ext/mmd/critic_markup.c +100 -4
  6. data/ext/mmd/critic_markup.h +7 -0
  7. data/ext/mmd/d_string.c +502 -119
  8. data/ext/mmd/epub.c +2 -4
  9. data/ext/mmd/file.c +436 -0
  10. data/ext/mmd/file.h +153 -0
  11. data/ext/mmd/html.c +130 -37
  12. data/ext/mmd/include/d_string.h +20 -19
  13. data/ext/mmd/include/libMultiMarkdown.h +42 -27
  14. data/ext/mmd/include/token.h +15 -15
  15. data/ext/mmd/latex.c +107 -30
  16. data/ext/mmd/lexer.c +19 -7
  17. data/ext/mmd/lexer.h +2 -2
  18. data/ext/mmd/memoir.c +29 -0
  19. data/ext/mmd/mmd.c +65 -39
  20. data/ext/mmd/object_pool.h +4 -4
  21. data/ext/mmd/opendocument-content.c +95 -13
  22. data/ext/mmd/opendocument.c +315 -313
  23. data/ext/mmd/opml-lexer.c +2183 -0
  24. data/ext/mmd/opml-lexer.h +157 -0
  25. data/ext/mmd/opml-parser.c +1193 -0
  26. data/ext/mmd/opml-parser.h +15 -0
  27. data/ext/mmd/opml-reader.c +435 -0
  28. data/ext/mmd/opml-reader.h +111 -0
  29. data/ext/mmd/opml.c +511 -0
  30. data/ext/mmd/opml.h +115 -0
  31. data/ext/mmd/parser.c +2 -0
  32. data/ext/mmd/rng.c +1 -1
  33. data/ext/mmd/scanners.c +51663 -24824
  34. data/ext/mmd/stack.c +4 -2
  35. data/ext/mmd/stack.h +8 -8
  36. data/ext/mmd/textbundle.c +2 -4
  37. data/ext/mmd/token.c +24 -12
  38. data/ext/mmd/token_pairs.c +2 -2
  39. data/ext/mmd/token_pairs.h +10 -10
  40. data/ext/mmd/transclude.c +1 -226
  41. data/ext/mmd/transclude.h +0 -8
  42. data/ext/mmd/uuid.c +3 -3
  43. data/ext/mmd/version.h +3 -3
  44. data/ext/mmd/writer.c +99 -30
  45. data/ext/mmd/writer.h +11 -0
  46. data/lib/multi_markdown.bundle +0 -0
  47. data/lib/multi_markdown/version.rb +1 -1
  48. metadata +13 -5
  49. data/ext/mmd/fodt.c +0 -2288
  50. data/ext/mmd/fodt.h +0 -81
@@ -0,0 +1,153 @@
1
+ /**
2
+
3
+ MultiMarkdown -- Lightweight markup processor to produce HTML, LaTeX, and more.
4
+
5
+ @file file.h
6
+
7
+ @brief
8
+
9
+
10
+ @author Fletcher T. Penney
11
+ @bug
12
+
13
+ **/
14
+
15
+ /*
16
+
17
+ Copyright © 2016 - 2017 Fletcher T. Penney.
18
+
19
+
20
+ The `MultiMarkdown 6` project is released under the MIT License..
21
+
22
+ GLibFacade.c and GLibFacade.h are from the MultiMarkdown v4 project:
23
+
24
+ https://github.com/fletcher/MultiMarkdown-4/
25
+
26
+ MMD 4 is released under both the MIT License and GPL.
27
+
28
+
29
+ CuTest is released under the zlib/libpng license. See CuTest.c for the
30
+ text of the license.
31
+
32
+ uthash library:
33
+ Copyright (c) 2005-2016, Troy D. Hanson
34
+
35
+ Licensed under Revised BSD license
36
+
37
+ miniz library:
38
+ Copyright 2013-2014 RAD Game Tools and Valve Software
39
+ Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
40
+
41
+ Licensed under the MIT license
42
+
43
+ argtable3 library:
44
+ Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
45
+ <sheitmann@users.sourceforge.net>
46
+ All rights reserved.
47
+
48
+ Licensed under the Revised BSD License
49
+
50
+
51
+ ## The MIT License ##
52
+
53
+ Permission is hereby granted, free of charge, to any person obtaining
54
+ a copy of this software and associated documentation files (the
55
+ "Software"), to deal in the Software without restriction, including
56
+ without limitation the rights to use, copy, modify, merge, publish,
57
+ distribute, sublicense, and/or sell copies of the Software, and to
58
+ permit persons to whom the Software is furnished to do so, subject to
59
+ the following conditions:
60
+
61
+ The above copyright notice and this permission notice shall be
62
+ included in all copies or substantial portions of the Software.
63
+
64
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
65
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
66
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
67
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
68
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
69
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
70
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
71
+
72
+
73
+ ## Revised BSD License ##
74
+
75
+ Redistribution and use in source and binary forms, with or without
76
+ modification, are permitted provided that the following conditions are
77
+ met:
78
+ * Redistributions of source code must retain the above copyright
79
+ notice, this list of conditions and the following disclaimer.
80
+ * Redistributions in binary form must reproduce the above
81
+ copyright notice, this list of conditions and the following
82
+ disclaimer in the documentation and/or other materials provided
83
+ with the distribution.
84
+ * Neither the name of the <organization> nor the
85
+ names of its contributors may be used to endorse or promote
86
+ products derived from this software without specific prior
87
+ written permission.
88
+
89
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
90
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
91
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
92
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT
93
+ HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
94
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
95
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES LOSS OF USE, DATA, OR
96
+ PROFITS OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
97
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
98
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
99
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
100
+
101
+
102
+ */
103
+
104
+
105
+ #ifndef FILE_UTILITIES_MULTIMARKDOWN_H
106
+ #define FILE_UTILITIES_MULTIMARKDOWN_H
107
+
108
+ #include <stdbool.h>
109
+
110
+ #ifdef TEST
111
+ #include "CuTest.h"
112
+ #endif
113
+
114
+
115
+ /// From d_string.h:
116
+ typedef struct DString DString;
117
+
118
+
119
+ /// Scan file into a DString
120
+ DString * scan_file(const char * fname);
121
+
122
+
123
+ /// Scan from stdin into a DString
124
+ DString * stdin_buffer();
125
+
126
+
127
+ /// Windows can use either `\` or `/` as a separator -- thanks to t-beckmann on github
128
+ /// for suggesting a fix for this.
129
+ bool is_separator(char c);
130
+
131
+
132
+ /// Ensure that path ends in separator
133
+ void add_trailing_sep(DString * path);
134
+
135
+
136
+ /// Combine directory and base filename to create a full path */
137
+ char * path_from_dir_base(const char * dir, const char * base);
138
+
139
+
140
+ /// Separate filename and directory from a full path
141
+ void split_path_file(char ** dir, char ** file, const char * path);
142
+
143
+
144
+ // Convert argument to absolute path
145
+ char * absolute_path_for_argument(const char * arg);
146
+
147
+
148
+ #if (defined(_WIN32) || defined(__WIN32__))
149
+ // Windows does not know realpath(), so we need a "windows port"
150
+ char *realpath(const char *path, char *resolved_path);
151
+ #endif
152
+
153
+ #endif
@@ -113,6 +113,11 @@ void mmd_print_char_html(DString * out, char c, bool obfuscate) {
113
113
  print_const("&gt;");
114
114
  break;
115
115
 
116
+ case '\n':
117
+ case '\r':
118
+ print_const("<br/>\n");
119
+ break;
120
+
116
121
  default:
117
122
  if (obfuscate && ((int) c == (((int) c) & 127))) {
118
123
  if (ran_num_next() % 2 == 0) {
@@ -209,6 +214,7 @@ void mmd_print_localized_char_html(DString * out, unsigned short type, scratch_p
209
214
  break;
210
215
 
211
216
  case FRENCH:
217
+ case SPANISH:
212
218
  print_const("&#171;");
213
219
  break;
214
220
 
@@ -233,6 +239,7 @@ void mmd_print_localized_char_html(DString * out, unsigned short type, scratch_p
233
239
  break;
234
240
 
235
241
  case FRENCH:
242
+ case SPANISH:
236
243
  print_const("&#187;");
237
244
  break;
238
245
 
@@ -257,11 +264,6 @@ static char * strip_dimension_units(char *original) {
257
264
  result[i] = tolower(result[i]);
258
265
  }
259
266
 
260
- if (strstr(&result[strlen(result) - 2], "px")) {
261
- // Leave 'px' alone
262
- return result;
263
- }
264
-
265
267
  // Trim anything other than digits
266
268
  for (i = 0; result[i]; i++) {
267
269
  if (result[i] < '0' || result[i] > '9') {
@@ -371,6 +373,12 @@ void mmd_export_image_html(DString * out, const char * source, token * text, lin
371
373
  if (strcmp(a->key, "width") == 0) {
372
374
  width = strip_dimension_units(a->value);
373
375
 
376
+ if (strlen(width) + 2 == strlen(a->value)) {
377
+ if (strcmp(&(a->value[strlen(width)]), "px") == 0) {
378
+ a->value[strlen(width)] = '\0';
379
+ }
380
+ }
381
+
374
382
  if (strcmp(a->value, width) == 0) {
375
383
  print_const(" ");
376
384
  print(a->key);
@@ -386,6 +394,12 @@ void mmd_export_image_html(DString * out, const char * source, token * text, lin
386
394
  } else if (strcmp(a->key, "height") == 0) {
387
395
  height = strip_dimension_units(a->value);
388
396
 
397
+ if (strlen(height) + 2 == strlen(a->value)) {
398
+ if (strcmp(&(a->value[strlen(height)]), "px") == 0) {
399
+ a->value[strlen(height)] = '\0';
400
+ }
401
+ }
402
+
389
403
  if (strcmp(a->value, height) == 0) {
390
404
  print_const(" ");
391
405
  print(a->key);
@@ -673,6 +687,8 @@ void mmd_export_token_html(DString * out, const char * source, token * t, scratc
673
687
  }
674
688
 
675
689
  mmd_export_token_tree_html(out, source, t->child, scratch);
690
+ trim_trailing_whitespace_d_string(out);
691
+
676
692
  printf("</h%1d>", temp_short + scratch->base_header_level - 1);
677
693
  scratch->padded = 0;
678
694
  break;
@@ -883,7 +899,7 @@ void mmd_export_token_html(DString * out, const char * source, token * t, scratc
883
899
  temp_token = t->next->child;
884
900
 
885
901
  if (temp_token->next &&
886
- temp_token->next->type == PAIR_BRACKET) {
902
+ temp_token->next->type == PAIR_BRACKET) {
887
903
  temp_token = temp_token->next;
888
904
  }
889
905
 
@@ -1125,7 +1141,7 @@ void mmd_export_token_html(DString * out, const char * source, token * t, scratc
1125
1141
 
1126
1142
  case ESCAPED_CHARACTER:
1127
1143
  if (!(scratch->extensions & EXT_COMPATIBILITY) &&
1128
- (source[t->start + 1] == ' ')) {
1144
+ (source[t->start + 1] == ' ')) {
1129
1145
  print_const("&nbsp;");
1130
1146
  } else {
1131
1147
  mmd_print_char_html(out, source[t->start + 1], false);
@@ -1344,7 +1360,7 @@ void mmd_export_token_html(DString * out, const char * source, token * t, scratc
1344
1360
 
1345
1361
  case PAIR_BRACKET:
1346
1362
  if ((scratch->extensions & EXT_NOTES) &&
1347
- (t->next && t->next->type == PAIR_BRACKET_CITATION)) {
1363
+ (t->next && t->next->type == PAIR_BRACKET_CITATION)) {
1348
1364
  goto parse_citation;
1349
1365
  }
1350
1366
 
@@ -1360,8 +1376,8 @@ void mmd_export_token_html(DString * out, const char * source, token * t, scratc
1360
1376
  temp_token = t->next;
1361
1377
 
1362
1378
  if (temp_token &&
1363
- ((temp_token->type == PAIR_BRACKET) ||
1364
- (temp_token->type == PAIR_PAREN))) {
1379
+ ((temp_token->type == PAIR_BRACKET) ||
1380
+ (temp_token->type == PAIR_PAREN))) {
1365
1381
  temp_token = temp_token->next;
1366
1382
  }
1367
1383
 
@@ -1519,11 +1535,11 @@ parse_citation:
1519
1535
  if (temp_short2 == scratch->used_citations->size) {
1520
1536
  // This is a re-use of a previously used note
1521
1537
  printf("<a href=\"#cn:%d\" title=\"%s\" class=\"citation\">(%d)</a>",
1522
- temp_short, LC("see citation"), temp_short);
1538
+ temp_short, LC("see citation"), temp_short);
1523
1539
  } else {
1524
1540
  // This is the first time this note was used
1525
1541
  printf("<a href=\"#cn:%d\" id=\"cnref:%d\" title=\"%s\" class=\"citation\">(%d)</a>",
1526
- temp_short, temp_short, LC("see citation"), temp_short);
1542
+ temp_short, temp_short, LC("see citation"), temp_short);
1527
1543
  }
1528
1544
  } else {
1529
1545
  // Locator present
@@ -1531,11 +1547,11 @@ parse_citation:
1531
1547
  if (temp_short2 == scratch->used_citations->size) {
1532
1548
  // This is a re-use of a previously used note
1533
1549
  printf("<a href=\"#cn:%d\" title=\"%s\" class=\"citation\">(%s, %d)</a>",
1534
- temp_short, LC("see citation"), temp_char, temp_short);
1550
+ temp_short, LC("see citation"), temp_char, temp_short);
1535
1551
  } else {
1536
1552
  // This is the first time this note was used
1537
1553
  printf("<a href=\"#cn:%d\" id=\"cnref:%d\" title=\"%s\" class=\"citation\">(%s, %d)</a>",
1538
- temp_short, temp_short, LC("see citation"), temp_char, temp_short);
1554
+ temp_short, temp_short, LC("see citation"), temp_char, temp_short);
1539
1555
  }
1540
1556
  }
1541
1557
  } else {
@@ -1582,7 +1598,7 @@ parse_citation:
1582
1598
  }
1583
1599
 
1584
1600
  printf("<a href=\"#fn:%d\" title=\"%s\" class=\"footnote\"><sup>%d</sup></a>",
1585
- temp_short3, LC("see footnote"), temp_short);
1601
+ temp_short3, LC("see footnote"), temp_short);
1586
1602
  } else {
1587
1603
  // This is the first time this note was used
1588
1604
 
@@ -1594,7 +1610,7 @@ parse_citation:
1594
1610
  }
1595
1611
 
1596
1612
  printf("<a href=\"#fn:%d\" id=\"fnref:%d\" title=\"%s\" class=\"footnote\"><sup>%d</sup></a>",
1597
- temp_short3, temp_short3, LC("see footnote"), temp_short);
1613
+ temp_short3, temp_short3, LC("see footnote"), temp_short);
1598
1614
  }
1599
1615
  } else {
1600
1616
  // Note-based syntax disabled
@@ -1634,7 +1650,7 @@ parse_citation:
1634
1650
  // This is a re-use of a previously used note
1635
1651
 
1636
1652
  printf("<a href=\"#gn:%d\" title=\"%s\" class=\"glossary\">",
1637
- temp_short, LC("see glossary"));
1653
+ temp_short, LC("see glossary"));
1638
1654
  mmd_print_string_html(out, temp_note->clean_text, false);
1639
1655
  print_const("</a>");
1640
1656
  } else {
@@ -1642,7 +1658,7 @@ parse_citation:
1642
1658
 
1643
1659
 
1644
1660
  printf("<a href=\"#gn:%d\" id=\"gnref:%d\" title=\"%s\" class=\"glossary\">",
1645
- temp_short, temp_short, LC("see glossary"));
1661
+ temp_short, temp_short, LC("see glossary"));
1646
1662
  mmd_print_string_html(out, temp_note->clean_text, false);
1647
1663
  print_const("</a>");
1648
1664
  }
@@ -1719,7 +1735,7 @@ parse_citation:
1719
1735
 
1720
1736
  // Ignore if we're rejecting or accepting
1721
1737
  if ((scratch->extensions & EXT_CRITIC_REJECT) ||
1722
- (scratch->extensions & EXT_CRITIC_ACCEPT)) {
1738
+ (scratch->extensions & EXT_CRITIC_ACCEPT)) {
1723
1739
  break;
1724
1740
  }
1725
1741
 
@@ -1739,7 +1755,7 @@ parse_citation:
1739
1755
 
1740
1756
  // Ignore if we're rejecting or accepting
1741
1757
  if ((scratch->extensions & EXT_CRITIC_REJECT) ||
1742
- (scratch->extensions & EXT_CRITIC_ACCEPT)) {
1758
+ (scratch->extensions & EXT_CRITIC_ACCEPT)) {
1743
1759
  t->child->type = TEXT_EMPTY;
1744
1760
  t->child->mate->type = TEXT_EMPTY;
1745
1761
  mmd_export_token_tree_html(out, source, t->child, scratch);
@@ -1768,8 +1784,8 @@ parse_citation:
1768
1784
 
1769
1785
  case PAIR_CRITIC_SUB_DEL:
1770
1786
  if ((scratch->extensions & EXT_CRITIC) &&
1771
- (t->next) &&
1772
- (t->next->type == PAIR_CRITIC_SUB_ADD)) {
1787
+ (t->next) &&
1788
+ (t->next->type == PAIR_CRITIC_SUB_ADD)) {
1773
1789
  t->child->type = TEXT_EMPTY;
1774
1790
  t->child->mate->type = TEXT_EMPTY;
1775
1791
 
@@ -1790,8 +1806,8 @@ parse_citation:
1790
1806
 
1791
1807
  case PAIR_CRITIC_SUB_ADD:
1792
1808
  if ((scratch->extensions & EXT_CRITIC) &&
1793
- (t->prev) &&
1794
- (t->prev->type == PAIR_CRITIC_SUB_DEL)) {
1809
+ (t->prev) &&
1810
+ (t->prev->type == PAIR_CRITIC_SUB_DEL)) {
1795
1811
  t->child->type = TEXT_EMPTY;
1796
1812
  t->child->mate->type = TEXT_EMPTY;
1797
1813
 
@@ -2044,6 +2060,8 @@ void mmd_export_token_html_raw(DString * out, const char * source, token * t, sc
2044
2060
  return;
2045
2061
  }
2046
2062
 
2063
+ char * temp;
2064
+
2047
2065
  switch (t->type) {
2048
2066
  case BACKTICK:
2049
2067
  print_token(t);
@@ -2065,9 +2083,42 @@ void mmd_export_token_html_raw(DString * out, const char * source, token * t, sc
2065
2083
  print_const("&lt;");
2066
2084
  break;
2067
2085
 
2086
+ case CRITIC_COM_OPEN:
2087
+ print_const("{&gt;&gt;");
2088
+ break;
2089
+
2090
+ case CRITIC_COM_CLOSE:
2091
+ print_const("&lt;&lt;}");
2092
+ break;
2093
+
2094
+ case CRITIC_SUB_DIV:
2095
+ print_const("~&gt;");
2096
+ break;
2097
+
2098
+ case CRITIC_SUB_DIV_A:
2099
+ print_const("~");
2100
+ break;
2101
+
2102
+ case CRITIC_SUB_DIV_B:
2103
+ print_const("&gt;");
2104
+ break;
2105
+
2068
2106
  case ESCAPED_CHARACTER:
2069
2107
  print_const("\\");
2070
- mmd_print_char_html(out, source[t->start + 1], false);
2108
+
2109
+ if (t->next && t->next->type == TEXT_EMPTY && source[t->start + 1] == ' ') {
2110
+ } else {
2111
+ mmd_print_char_html(out, source[t->start + 1], false);
2112
+ }
2113
+
2114
+ break;
2115
+
2116
+ case HTML_COMMENT_START:
2117
+ print_const("&lt;!--");
2118
+ break;
2119
+
2120
+ case HTML_COMMENT_STOP:
2121
+ print_const("--&gt;");
2071
2122
  break;
2072
2123
 
2073
2124
  case HTML_ENTITY:
@@ -2075,6 +2126,27 @@ void mmd_export_token_html_raw(DString * out, const char * source, token * t, sc
2075
2126
  d_string_append_c_array(out, &(source[t->start + 1]), t->len - 1);
2076
2127
  break;
2077
2128
 
2129
+ case MARKER_LIST_BULLET:
2130
+ case MARKER_LIST_ENUMERATOR:
2131
+ print_token(t);
2132
+
2133
+ temp = NULL;
2134
+
2135
+ if (t->next) {
2136
+ temp = (char *) &source[t->next->start];
2137
+ }
2138
+
2139
+ source = (char *) &source[t->start + t->len];
2140
+
2141
+ while (char_is_whitespace(*source) &&
2142
+ ((temp == NULL) ||
2143
+ (source < temp))) {
2144
+ print_char(*source);
2145
+ source++;
2146
+ }
2147
+
2148
+ break;
2149
+
2078
2150
  case MATH_BRACKET_OPEN:
2079
2151
  print_const("\\\\[");
2080
2152
  break;
@@ -2084,21 +2156,11 @@ void mmd_export_token_html_raw(DString * out, const char * source, token * t, sc
2084
2156
  break;
2085
2157
 
2086
2158
  case MATH_DOLLAR_SINGLE:
2087
- if (t->mate) {
2088
- (t->start < t->mate->start) ? ( print_const("\\(") ) : ( print_const("\\)") );
2089
- } else {
2090
- print_const("$");
2091
- }
2092
-
2159
+ print_const("$");
2093
2160
  break;
2094
2161
 
2095
2162
  case MATH_DOLLAR_DOUBLE:
2096
- if (t->mate) {
2097
- (t->start < t->mate->start) ? ( print_const("\\[") ) : ( print_const("\\]") );
2098
- } else {
2099
- print_const("$$");
2100
- }
2101
-
2163
+ print_const("$$");
2102
2164
  break;
2103
2165
 
2104
2166
  case MATH_PAREN_OPEN:
@@ -2167,6 +2229,24 @@ void mmd_export_token_html_math(DString * out, const char * source, token * t, s
2167
2229
  print_const("\\]");
2168
2230
  break;
2169
2231
 
2232
+ case MATH_DOLLAR_SINGLE:
2233
+ if (t->mate) {
2234
+ (t->start < t->mate->start) ? ( print_const("\\(") ) : ( print_const("\\)") );
2235
+ } else {
2236
+ print_const("$");
2237
+ }
2238
+
2239
+ break;
2240
+
2241
+ case MATH_DOLLAR_DOUBLE:
2242
+ if (t->mate) {
2243
+ (t->start < t->mate->start) ? ( print_const("\\[") ) : ( print_const("\\]") );
2244
+ } else {
2245
+ print_const("$$");
2246
+ }
2247
+
2248
+ break;
2249
+
2170
2250
  case MATH_PAREN_OPEN:
2171
2251
  print_const("\\(");
2172
2252
  break;
@@ -2248,12 +2328,14 @@ void mmd_start_complete_html(DString * out, const char * source, scratch_pad * s
2248
2328
  } else if (strcmp(m->key, "latexbegin") == 0) {
2249
2329
  } else if (strcmp(m->key, "latexconfig") == 0) {
2250
2330
  } else if (strcmp(m->key, "latexfooter") == 0) {
2331
+ } else if (strcmp(m->key, "latexheader") == 0) {
2251
2332
  } else if (strcmp(m->key, "latexheaderlevel") == 0) {
2252
2333
  } else if (strcmp(m->key, "latexinput") == 0) {
2253
2334
  } else if (strcmp(m->key, "latexleader") == 0) {
2254
2335
  } else if (strcmp(m->key, "latexmode") == 0) {
2255
2336
  } else if (strcmp(m->key, "mmdfooter") == 0) {
2256
2337
  } else if (strcmp(m->key, "mmdheader") == 0) {
2338
+ } else if (strcmp(m->key, "odfheader") == 0) {
2257
2339
  } else if (strcmp(m->key, "quoteslanguage") == 0) {
2258
2340
  } else if (strcmp(m->key, "title") == 0) {
2259
2341
  print_const("\t<title>");
@@ -2278,6 +2360,17 @@ void mmd_start_complete_html(DString * out, const char * source, scratch_pad * s
2278
2360
 
2279
2361
 
2280
2362
  void mmd_end_complete_html(DString * out, const char * source, scratch_pad * scratch) {
2363
+ meta * m;
2364
+
2365
+ // Iterate over metadata keys
2366
+
2367
+ for (m = scratch->meta_hash; m != NULL; m = m->hh.next) {
2368
+ if (strcmp(m->key, "htmlfooter") == 0) {
2369
+ print(m->value);
2370
+ print_char('\n');
2371
+ }
2372
+ }
2373
+
2281
2374
  print_const("\n\n</body>\n</html>\n");
2282
2375
  }
2283
2376