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
@@ -1,4 +1,4 @@
1
- /* Generated by re2c 0.14.3 on Sun Jul 23 17:28:33 2017 */
1
+ /* Generated by re2c 0.14.3 on Wed May 23 14:18:41 2018 */
2
2
  /**
3
3
 
4
4
  MultiMarkdown 6 -- Lightweight markup processor to produce HTML, LaTeX, and more.
@@ -643,6 +643,7 @@ yy53:
643
643
  yyaccept = 8;
644
644
  yych = *(YYMARKER = ++YYCURSOR);
645
645
  switch (yych) {
646
+ case 0x00:
646
647
  case '\t':
647
648
  case '\n':
648
649
  case '\r':
@@ -1088,6 +1089,7 @@ yy86:
1088
1089
  YYCTXMARKER = YYCURSOR + 1;
1089
1090
  yych = *++YYCURSOR;
1090
1091
  switch (yych) {
1092
+ case 0x00:
1091
1093
  case '\t':
1092
1094
  case '\n':
1093
1095
  case '\r':
@@ -1102,9 +1104,10 @@ yy87:
1102
1104
  yych = *YYCURSOR;
1103
1105
  yy88:
1104
1106
  switch (yych) {
1107
+ case 0x00:
1108
+ case '\n': goto yy91;
1105
1109
  case '\t':
1106
1110
  case ' ': goto yy87;
1107
- case '\n': goto yy91;
1108
1111
  case '\r': goto yy93;
1109
1112
  case 0xC2: goto yy90;
1110
1113
  default: goto yy89;
@@ -1135,9 +1138,10 @@ yy94:
1135
1138
  yych = *YYCURSOR;
1136
1139
  yy95:
1137
1140
  switch (yych) {
1141
+ case 0x00:
1142
+ case '\n': goto yy98;
1138
1143
  case '\t':
1139
1144
  case ' ': goto yy94;
1140
- case '\n': goto yy98;
1141
1145
  case '\r': goto yy100;
1142
1146
  case 0xC2: goto yy97;
1143
1147
  default: goto yy96;
@@ -1166,6 +1170,7 @@ yy101:
1166
1170
  YYCTXMARKER = YYCURSOR + 1;
1167
1171
  yych = *++YYCURSOR;
1168
1172
  switch (yych) {
1173
+ case 0x00:
1169
1174
  case '\t':
1170
1175
  case '\n':
1171
1176
  case '\r':
@@ -1178,6 +1183,7 @@ yy102:
1178
1183
  YYCTXMARKER = YYCURSOR + 1;
1179
1184
  yych = *++YYCURSOR;
1180
1185
  switch (yych) {
1186
+ case 0x00:
1181
1187
  case '\t':
1182
1188
  case '\n':
1183
1189
  case '\r':
@@ -1192,9 +1198,10 @@ yy103:
1192
1198
  yych = *YYCURSOR;
1193
1199
  yy104:
1194
1200
  switch (yych) {
1201
+ case 0x00:
1202
+ case '\n': goto yy107;
1195
1203
  case '\t':
1196
1204
  case ' ': goto yy103;
1197
- case '\n': goto yy107;
1198
1205
  case '\r': goto yy109;
1199
1206
  case 0xC2: goto yy106;
1200
1207
  default: goto yy105;
@@ -1225,9 +1232,10 @@ yy110:
1225
1232
  yych = *YYCURSOR;
1226
1233
  yy111:
1227
1234
  switch (yych) {
1235
+ case 0x00:
1236
+ case '\n': goto yy114;
1228
1237
  case '\t':
1229
1238
  case ' ': goto yy110;
1230
- case '\n': goto yy114;
1231
1239
  case '\r': goto yy116;
1232
1240
  case 0xC2: goto yy113;
1233
1241
  default: goto yy112;
@@ -1256,6 +1264,7 @@ yy117:
1256
1264
  YYCTXMARKER = YYCURSOR + 1;
1257
1265
  yych = *++YYCURSOR;
1258
1266
  switch (yych) {
1267
+ case 0x00:
1259
1268
  case '\t':
1260
1269
  case '\n':
1261
1270
  case '\r':
@@ -1268,6 +1277,7 @@ yy118:
1268
1277
  YYCTXMARKER = YYCURSOR + 1;
1269
1278
  yych = *++YYCURSOR;
1270
1279
  switch (yych) {
1280
+ case 0x00:
1271
1281
  case '\t':
1272
1282
  case '\n':
1273
1283
  case '\r':
@@ -1281,9 +1291,10 @@ yy119:
1281
1291
  yych = *YYCURSOR;
1282
1292
  yy120:
1283
1293
  switch (yych) {
1294
+ case 0x00:
1295
+ case '\n': goto yy123;
1284
1296
  case '\t':
1285
1297
  case ' ': goto yy119;
1286
- case '\n': goto yy123;
1287
1298
  case '\r': goto yy125;
1288
1299
  case 0xC2: goto yy122;
1289
1300
  default: goto yy121;
@@ -1314,9 +1325,10 @@ yy126:
1314
1325
  yych = *YYCURSOR;
1315
1326
  yy127:
1316
1327
  switch (yych) {
1328
+ case 0x00:
1329
+ case '\n': goto yy130;
1317
1330
  case '\t':
1318
1331
  case ' ': goto yy126;
1319
- case '\n': goto yy130;
1320
1332
  case '\r': goto yy132;
1321
1333
  case 0xC2: goto yy129;
1322
1334
  default: goto yy128;
@@ -69,7 +69,7 @@ typedef struct Scanner Scanner;
69
69
 
70
70
  /// Scan for the next token
71
71
  int scan(
72
- Scanner * s, //!< Pointer to Scanner state structure
73
- const char * stop //!< Pointer to position in string at which to stop parsing
72
+ Scanner * s, //!< Pointer to Scanner state structure
73
+ const char * stop //!< Pointer to position in string at which to stop parsing
74
74
  );
75
75
 
@@ -55,6 +55,7 @@
55
55
 
56
56
  #include "latex.h"
57
57
  #include "memoir.h"
58
+ #include "parser.h"
58
59
 
59
60
  #define print(x) d_string_append(out, x)
60
61
  #define print_const(x) d_string_append_c_array(out, x, sizeof(x) - 1)
@@ -70,6 +71,7 @@ void mmd_export_token_memoir(DString * out, const char * source, token * t, scra
70
71
  }
71
72
 
72
73
  char * temp_char = NULL;
74
+ token * temp_token = NULL;
73
75
 
74
76
  switch (t->type) {
75
77
  case DOC_START_TOKEN:
@@ -82,6 +84,33 @@ void mmd_export_token_memoir(DString * out, const char * source, token * t, scra
82
84
  temp_char = get_fence_language_specifier(t->child->child, source);
83
85
 
84
86
  if (temp_char) {
87
+ if (strncmp("{=", temp_char, 2) == 0) {
88
+ // Raw source
89
+ if (raw_filter_text_matches(temp_char, FORMAT_MEMOIR)) {
90
+ switch (t->child->tail->type) {
91
+ case LINE_FENCE_BACKTICK_3:
92
+ case LINE_FENCE_BACKTICK_4:
93
+ case LINE_FENCE_BACKTICK_5:
94
+ temp_token = t->child->tail;
95
+ break;
96
+
97
+ default:
98
+ temp_token = NULL;
99
+ }
100
+
101
+ if (temp_token) {
102
+ d_string_append_c_array(out, &source[t->child->next->start], temp_token->start - t->child->next->start);
103
+ scratch->padded = 1;
104
+ } else {
105
+ d_string_append_c_array(out, &source[t->child->start + t->child->len], t->start + t->len - t->child->next->start);
106
+ scratch->padded = 0;
107
+ }
108
+ }
109
+
110
+ free(temp_char);
111
+ break;
112
+ }
113
+
85
114
  printf("\\begin{adjustwidth}{2.5em}{2.5em}\n\\begin{lstlisting}[language=%s]\n", temp_char);
86
115
  } else {
87
116
  print_const("\\begin{adjustwidth}{2.5em}{2.5em}\n\\begin{verbatim}\n");
@@ -65,6 +65,7 @@
65
65
  #include "mmd.h"
66
66
  #include "object_pool.h"
67
67
  #include "opendocument.h"
68
+ #include "opml-reader.h"
68
69
  #include "parser.h"
69
70
  #include "scanners.h"
70
71
  #include "stack.h"
@@ -115,6 +116,10 @@ mmd_engine * mmd_engine_create(DString * d, unsigned long extensions) {
115
116
 
116
117
  e->allow_meta = (extensions & EXT_COMPATIBILITY) ? false : true;
117
118
 
119
+ if (e->allow_meta) {
120
+ e->allow_meta = (extensions & EXT_NO_METADATA) ? false : true;
121
+ }
122
+
118
123
  e->language = LC_EN;
119
124
  e->quotes_lang = ENGLISH;
120
125
 
@@ -237,7 +242,7 @@ void mmd_engine_set_language(mmd_engine * e, short language) {
237
242
  break;
238
243
 
239
244
  case LC_ES:
240
- e->quotes_lang = ENGLISH;
245
+ e->quotes_lang = SPANISH;
241
246
  break;
242
247
 
243
248
  case LC_FR:
@@ -506,20 +511,17 @@ void mmd_assign_line_type(mmd_engine * e, token * line) {
506
511
  line->type = (first_child->type - HASH1) + LINE_ATX_1;
507
512
  first_child->type = (line->type - LINE_ATX_1) + MARKER_H1;
508
513
 
509
- // Strip trailing whitespace from '#' sequence
510
- first_child->len = first_child->type - MARKER_H1 + 1;
511
-
512
514
  // Strip trailing '#' sequence if present
513
515
  if (line->child->tail->type == TEXT_NL) {
514
516
  if ((line->child->tail->prev->type >= HASH1) &&
515
- (line->child->tail->prev->type <= HASH6)) {
517
+ (line->child->tail->prev->type <= HASH6)) {
516
518
  line->child->tail->prev->type -= HASH1;
517
519
  line->child->tail->prev->type += MARKER_H1;
518
520
  }
519
521
  } else {
520
522
  if ((line->child->tail->type >= HASH1) &&
521
- (line->child->tail->type <= HASH6)) {
522
- line->child->tail->type -= TEXT_EMPTY;
523
+ (line->child->tail->type <= HASH6)) {
524
+ line->child->tail->type -= HASH1;
523
525
  line->child->tail->type += MARKER_H1;
524
526
  }
525
527
  }
@@ -572,8 +574,8 @@ void mmd_assign_line_type(mmd_engine * e, token * line) {
572
574
  t = first_child;
573
575
 
574
576
  while (t->next && ((t->next->type == INDENT_SPACE) ||
575
- (t->next->type == INDENT_TAB) ||
576
- (t->next->type == NON_INDENT_SPACE))) {
577
+ (t->next->type == INDENT_TAB) ||
578
+ (t->next->type == NON_INDENT_SPACE))) {
577
579
  tokens_prune(t->next, t->next);
578
580
  }
579
581
 
@@ -722,8 +724,8 @@ void mmd_assign_line_type(mmd_engine * e, token * line) {
722
724
  t = first_child;
723
725
 
724
726
  while (t->next && ((t->next->type == INDENT_SPACE) ||
725
- (t->next->type == INDENT_TAB) ||
726
- (t->next->type == NON_INDENT_SPACE))) {
727
+ (t->next->type == INDENT_TAB) ||
728
+ (t->next->type == NON_INDENT_SPACE))) {
727
729
  tokens_prune(t->next, t->next);
728
730
  }
729
731
 
@@ -832,7 +834,7 @@ void mmd_assign_line_type(mmd_engine * e, token * line) {
832
834
  }
833
835
 
834
836
  if ((line->type == LINE_PLAIN) &&
835
- !(e->extensions & EXT_COMPATIBILITY)) {
837
+ !(e->extensions & EXT_COMPATIBILITY)) {
836
838
  // Check if this is a potential table line
837
839
  token * walker = first_child;
838
840
 
@@ -966,6 +968,9 @@ token * mmd_tokenize_string(mmd_engine * e, size_t start, size_t len, bool stop_
966
968
  // Reset metadata flag
967
969
  e->allow_meta = (e->extensions & EXT_COMPATIBILITY) ? false : true;
968
970
 
971
+ if (e->allow_meta) {
972
+ e->allow_meta = (e->extensions & EXT_NO_METADATA) ? false : true;
973
+ }
969
974
 
970
975
  // Create a scanner (for re2c)
971
976
  Scanner s;
@@ -1253,7 +1258,7 @@ void mmd_assign_ambidextrous_tokens_in_block(mmd_engine * e, token * block, size
1253
1258
 
1254
1259
  // Do we treat this like metadata?
1255
1260
  if (!(e->extensions & EXT_COMPATIBILITY) &&
1256
- !(e->extensions & EXT_NO_METADATA)) {
1261
+ !(e->extensions & EXT_NO_METADATA)) {
1257
1262
  break;
1258
1263
  }
1259
1264
 
@@ -1281,6 +1286,9 @@ void mmd_assign_ambidextrous_tokens_in_block(mmd_engine * e, token * block, size
1281
1286
  case BLOCK_SETEXT_1:
1282
1287
  case BLOCK_SETEXT_2:
1283
1288
  case BLOCK_TABLE:
1289
+ case BLOCK_TABLE_SECTION:
1290
+ case TABLE_ROW:
1291
+ case TABLE_CELL:
1284
1292
  case BLOCK_TERM:
1285
1293
  case LINE_LIST_BULLETED:
1286
1294
  case LINE_LIST_ENUMERATED:
@@ -1478,14 +1486,14 @@ void mmd_assign_ambidextrous_tokens_in_block(mmd_engine * e, token * block, size
1478
1486
  offset = t->start;
1479
1487
 
1480
1488
  if (!((offset == 0) ||
1481
- (char_is_whitespace_or_line_ending_or_punctuation(str[offset - 1])) ||
1482
- (char_is_whitespace_or_line_ending_or_punctuation(str[offset + 1])))) {
1489
+ (char_is_whitespace_or_line_ending_or_punctuation(str[offset - 1])) ||
1490
+ (char_is_whitespace_or_line_ending_or_punctuation(str[offset + 1])))) {
1483
1491
  t->type = APOSTROPHE;
1484
1492
  break;
1485
1493
  }
1486
1494
 
1487
1495
  if (offset && (char_is_punctuation(str[offset - 1])) &&
1488
- (char_is_alphanumeric(str[offset + 1]))) {
1496
+ (char_is_alphanumeric(str[offset + 1]))) {
1489
1497
  // If possessive apostrophe, e.g. `x`'s
1490
1498
  if (str[offset + 1] == 's' || str[offset + 1] == 'S') {
1491
1499
  if (char_is_whitespace_or_line_ending_or_punctuation(str[offset + 2])) {
@@ -1520,7 +1528,7 @@ void mmd_assign_ambidextrous_tokens_in_block(mmd_engine * e, token * block, size
1520
1528
  if (t->len == 1) {
1521
1529
  // Check whether we have '1-2'
1522
1530
  if ((offset == 0) || (!char_is_digit(str[offset - 1])) ||
1523
- (!char_is_digit(str[offset + 1]))) {
1531
+ (!char_is_digit(str[offset + 1]))) {
1524
1532
  t->type = TEXT_PLAIN;
1525
1533
  }
1526
1534
  }
@@ -1566,18 +1574,14 @@ void mmd_assign_ambidextrous_tokens_in_block(mmd_engine * e, token * block, size
1566
1574
 
1567
1575
  offset = t->start;
1568
1576
 
1569
- // Look left -- no whitespace to left
1570
- if ((offset == 0) || (char_is_whitespace_or_line_ending_or_punctuation(str[offset - 1]))) {
1571
- t->can_open = 0;
1572
- }
1573
-
1577
+ // Can't close if whitespace to left
1574
1578
  if ((offset != 0) && (char_is_whitespace_or_line_ending(str[offset - 1]))) {
1575
1579
  t->can_close = 0;
1576
1580
  }
1577
1581
 
1578
1582
  offset = t->start + t->len;
1579
1583
 
1580
- if (char_is_whitespace_or_line_ending_or_punctuation(str[offset])) {
1584
+ if (char_is_whitespace_or_line_ending(str[offset])) {
1581
1585
  t->can_open = 0;
1582
1586
  }
1583
1587
 
@@ -1661,11 +1665,11 @@ void pair_emphasis_tokens(token * t) {
1661
1665
  closer = t->mate;
1662
1666
 
1663
1667
  if (t->next &&
1664
- (t->next->mate == closer->prev) &&
1665
- (t->type == t->next->type) &&
1666
- (t->next->mate != t) &&
1667
- (t->start + t->len == t->next->start) &&
1668
- (closer->start == closer->prev->start + closer->prev->len)) {
1668
+ (t->next->mate == closer->prev) &&
1669
+ (t->type == t->next->type) &&
1670
+ (t->next->mate != t) &&
1671
+ (t->start + t->len == t->next->start) &&
1672
+ (closer->start == closer->prev->start + closer->prev->len)) {
1669
1673
 
1670
1674
  // We have a strong pair
1671
1675
  t->type = STRONG_START;
@@ -1788,8 +1792,8 @@ void is_list_loose(token * list) {
1788
1792
  /// Is this actually an HTML block?
1789
1793
  void is_para_html(mmd_engine * e, token * block) {
1790
1794
  if ((block == NULL) ||
1791
- (block->child == NULL) ||
1792
- (block->child->type != LINE_PLAIN)) {
1795
+ (block->child == NULL) ||
1796
+ (block->child->type != LINE_PLAIN)) {
1793
1797
  return;
1794
1798
  }
1795
1799
 
@@ -2043,7 +2047,7 @@ void strip_line_tokens_from_block(mmd_engine * e, token * block) {
2043
2047
  case LINE_SETEXT_1:
2044
2048
  case LINE_SETEXT_2:
2045
2049
  if ((block->type == BLOCK_SETEXT_1) ||
2046
- (block->type == BLOCK_SETEXT_2)) {
2050
+ (block->type == BLOCK_SETEXT_2)) {
2047
2051
  temp = l->next;
2048
2052
  tokens_prune(l, l);
2049
2053
  l = temp;
@@ -2088,7 +2092,7 @@ handle_line:
2088
2092
 
2089
2093
  // If we're not a code block, strip additional indents
2090
2094
  if ((block->type != BLOCK_CODE_INDENTED) &&
2091
- (block->type != BLOCK_CODE_FENCED)) {
2095
+ (block->type != BLOCK_CODE_FENCED)) {
2092
2096
  while (l->child && ((l->child->type == INDENT_SPACE) || (l->child->type == INDENT_TAB))) {
2093
2097
  token_remove_first_child(l);
2094
2098
  }
@@ -2123,7 +2127,7 @@ handle_line:
2123
2127
 
2124
2128
  // Move children to parent
2125
2129
  // Add ':' back
2126
- if (e->dstr->str[l->child->start - 1] == ':') {
2130
+ if (l->child->start > 0 && e->dstr->str[l->child->start - 1] == ':') {
2127
2131
  temp = token_new(COLON, l->child->start - 1, 1);
2128
2132
  token_append_child(block, temp);
2129
2133
  }
@@ -2184,6 +2188,10 @@ token * mmd_engine_parse_substring(mmd_engine * e, size_t byte_start, size_t byt
2184
2188
  e->extensions |= EXT_NO_METADATA;
2185
2189
  }
2186
2190
 
2191
+ if (e->extensions & EXT_PARSE_OPML) {
2192
+ // Convert from OPML first (if not done earlier)
2193
+ mmd_convert_opml_string(e, byte_start, byte_len);
2194
+ }
2187
2195
 
2188
2196
  // Tokenize the string
2189
2197
  token * doc = mmd_tokenize_string(e, byte_start, byte_len, false);
@@ -2259,6 +2267,7 @@ bool mmd_d_string_has_metadata(DString * source, size_t * end) {
2259
2267
  /// Does the text have metadata?
2260
2268
  bool mmd_engine_has_metadata(mmd_engine * e, size_t * end) {
2261
2269
  bool result = false;
2270
+ token * old_root;
2262
2271
 
2263
2272
  if (!e) {
2264
2273
  return false;
@@ -2275,10 +2284,8 @@ bool mmd_engine_has_metadata(mmd_engine * e, size_t * end) {
2275
2284
  return false;
2276
2285
  }
2277
2286
 
2278
- // Free existing parse tree
2279
- if (e->root) {
2280
- token_tree_free(e->root);
2281
- }
2287
+ // Preserve existing parse tree (if any)
2288
+ old_root = e->root;
2282
2289
 
2283
2290
  // Tokenize the string (up until first empty line)
2284
2291
  token * doc = mmd_tokenize_string(e, 0, e->dstr->currentStringLength, true);
@@ -2298,6 +2305,9 @@ bool mmd_engine_has_metadata(mmd_engine * e, size_t * end) {
2298
2305
  token_tree_free(doc);
2299
2306
  }
2300
2307
 
2308
+ // Restore previous parse tree
2309
+ e->root = old_root;
2310
+
2301
2311
  return result;
2302
2312
  }
2303
2313
 
@@ -2399,6 +2409,10 @@ char * mmd_d_string_metavalue_for_key(DString * source, const char * key) {
2399
2409
  /// Grab metadata without processing entire document
2400
2410
  /// Returned char * does not need to be freed
2401
2411
  char * mmd_engine_metavalue_for_key(mmd_engine * e, const char * key) {
2412
+ if (e == NULL || key == NULL) {
2413
+ return NULL;
2414
+ }
2415
+
2402
2416
  if (e->metadata_stack->size == 0) {
2403
2417
  // Ensure we have checked for metadata
2404
2418
  if (!mmd_engine_has_metadata(e, NULL)) {
@@ -2538,7 +2552,11 @@ void mmd_engine_update_metavalue_for_key(mmd_engine * e, const char * key, const
2538
2552
 
2539
2553
  DString * temp = d_string_new(key);
2540
2554
  d_string_append(temp, ":\t");
2541
- d_string_append(temp, value);
2555
+
2556
+ if (value) {
2557
+ d_string_append(temp, value);
2558
+ }
2559
+
2542
2560
  d_string_append_c(temp, '\n');
2543
2561
 
2544
2562
  if (start != -1) {
@@ -2568,7 +2586,10 @@ void mmd_engine_update_metavalue_for_key(mmd_engine * e, const char * key, const
2568
2586
 
2569
2587
  d_string_erase(e->dstr, start, len);
2570
2588
  d_string_insert(e->dstr, start, "\n");
2571
- d_string_insert(e->dstr, start, value);
2589
+
2590
+ if (value) {
2591
+ d_string_insert(e->dstr, start, value);
2592
+ }
2572
2593
  } else if (meta_end != 0) {
2573
2594
  // We're appending metadata at the end
2574
2595
  d_string_insert(e->dstr, meta_end, temp->str);
@@ -2743,6 +2764,11 @@ DString * mmd_engine_convert_to_data(mmd_engine * e, short format, const char *
2743
2764
  DString * result = NULL;
2744
2765
 
2745
2766
  if (format == FORMAT_MMD) {
2767
+ if (e->extensions & EXT_PARSE_OPML) {
2768
+ // Convert from OPML first (if not done earlier)
2769
+ mmd_convert_opml_string(e, 0, e->dstr->currentStringLength);
2770
+ }
2771
+
2746
2772
  // Simply return text (transclusion is handled externally)
2747
2773
  d_string_append_c_array(output, e->dstr->str, e->dstr->currentStringLength);
2748
2774