rmultimarkdown 6.2.2.1 → 6.4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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