rmultimarkdown 4.7.1.1 → 6.2.2.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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +2 -2
  3. data/README.md +7 -9
  4. data/Rakefile +33 -18
  5. data/ext/Makefile +261 -0
  6. data/ext/extconf.rb +23 -3
  7. data/ext/mmd/aho-corasick.c +596 -0
  8. data/ext/mmd/aho-corasick.h +120 -0
  9. data/ext/mmd/beamer.c +344 -0
  10. data/ext/mmd/beamer.h +72 -0
  11. data/ext/mmd/char.c +156 -0
  12. data/ext/mmd/char.h +111 -0
  13. data/ext/mmd/char_lookup.c +212 -0
  14. data/ext/mmd/critic_markup.c +330 -0
  15. data/ext/mmd/critic_markup.h +94 -0
  16. data/ext/mmd/d_string.c +402 -0
  17. data/ext/mmd/epub.c +563 -0
  18. data/ext/mmd/epub.h +69 -0
  19. data/ext/mmd/fodt.c +2288 -0
  20. data/ext/mmd/fodt.h +81 -0
  21. data/ext/mmd/html.c +2460 -0
  22. data/ext/mmd/html.h +81 -0
  23. data/ext/mmd/i18n.h +170 -0
  24. data/ext/mmd/include/d_string.h +182 -0
  25. data/ext/mmd/include/libMultiMarkdown.h +548 -0
  26. data/ext/mmd/include/token.h +233 -0
  27. data/ext/mmd/latex.c +2435 -0
  28. data/ext/mmd/latex.h +83 -0
  29. data/ext/mmd/lexer.c +3001 -0
  30. data/ext/mmd/lexer.h +75 -0
  31. data/ext/mmd/memoir.c +138 -0
  32. data/ext/mmd/memoir.h +67 -0
  33. data/ext/mmd/miniz.c +7557 -0
  34. data/ext/mmd/miniz.h +1328 -0
  35. data/ext/mmd/mmd.c +2798 -0
  36. data/ext/mmd/mmd.h +120 -0
  37. data/ext/mmd/object_pool.c +141 -0
  38. data/ext/mmd/object_pool.h +101 -0
  39. data/ext/mmd/opendocument-content.c +2071 -0
  40. data/ext/mmd/opendocument-content.h +135 -0
  41. data/ext/mmd/opendocument.c +981 -0
  42. data/ext/mmd/opendocument.h +118 -0
  43. data/ext/mmd/parser.c +1760 -0
  44. data/ext/mmd/parser.h +39 -0
  45. data/{MultiMarkdown-4 → ext/mmd}/rng.c +90 -49
  46. data/ext/mmd/scanners.c +77512 -0
  47. data/ext/mmd/scanners.h +101 -0
  48. data/ext/mmd/stack.c +142 -0
  49. data/ext/mmd/stack.h +113 -0
  50. data/ext/mmd/textbundle.c +455 -0
  51. data/ext/mmd/textbundle.h +115 -0
  52. data/ext/mmd/token.c +773 -0
  53. data/ext/mmd/token_pairs.c +263 -0
  54. data/ext/mmd/token_pairs.h +123 -0
  55. data/ext/mmd/transclude.c +549 -0
  56. data/ext/mmd/transclude.h +87 -0
  57. data/ext/mmd/uthash.h +1074 -0
  58. data/ext/mmd/uuid.c +154 -0
  59. data/ext/mmd/uuid.h +77 -0
  60. data/ext/mmd/version.h +111 -0
  61. data/ext/mmd/writer.c +2652 -0
  62. data/ext/mmd/writer.h +260 -0
  63. data/ext/mmd/zip.c +210 -0
  64. data/ext/mmd/zip.h +120 -0
  65. data/ext/{multi_markdown.c → ruby_multi_markdown.c} +87 -18
  66. data/lib/multi_markdown.bundle +0 -0
  67. data/lib/multi_markdown.rb +5 -8
  68. data/lib/multi_markdown/version.rb +1 -1
  69. data/rmultimarkdown.gemspec +2 -2
  70. data/test/{extensions_test.rb.rb → extensions_test.rb} +10 -54
  71. data/test/multi_markdown_test.rb +13 -0
  72. metadata +67 -47
  73. data/MultiMarkdown-4/GLibFacade.c +0 -310
  74. data/MultiMarkdown-4/GLibFacade.h +0 -100
  75. data/MultiMarkdown-4/beamer.c +0 -182
  76. data/MultiMarkdown-4/beamer.h +0 -11
  77. data/MultiMarkdown-4/critic.c +0 -111
  78. data/MultiMarkdown-4/critic.h +0 -15
  79. data/MultiMarkdown-4/glib.h +0 -11
  80. data/MultiMarkdown-4/html.c +0 -1117
  81. data/MultiMarkdown-4/html.h +0 -14
  82. data/MultiMarkdown-4/latex.c +0 -1217
  83. data/MultiMarkdown-4/latex.h +0 -16
  84. data/MultiMarkdown-4/libMultiMarkdown.h +0 -177
  85. data/MultiMarkdown-4/lyx.c +0 -2265
  86. data/MultiMarkdown-4/lyx.h +0 -37
  87. data/MultiMarkdown-4/lyxbeamer.c +0 -265
  88. data/MultiMarkdown-4/lyxbeamer.h +0 -11
  89. data/MultiMarkdown-4/memoir.c +0 -80
  90. data/MultiMarkdown-4/memoir.h +0 -10
  91. data/MultiMarkdown-4/multimarkdown.c +0 -518
  92. data/MultiMarkdown-4/odf.c +0 -1222
  93. data/MultiMarkdown-4/odf.h +0 -18
  94. data/MultiMarkdown-4/opml.c +0 -189
  95. data/MultiMarkdown-4/opml.h +0 -15
  96. data/MultiMarkdown-4/parse_utilities.c +0 -884
  97. data/MultiMarkdown-4/parser.c +0 -16656
  98. data/MultiMarkdown-4/parser.h +0 -188
  99. data/MultiMarkdown-4/rtf.c +0 -665
  100. data/MultiMarkdown-4/rtf.h +0 -17
  101. data/MultiMarkdown-4/strtok.c +0 -56
  102. data/MultiMarkdown-4/strtok.h +0 -9
  103. data/MultiMarkdown-4/text.c +0 -53
  104. data/MultiMarkdown-4/text.h +0 -11
  105. data/MultiMarkdown-4/toc.c +0 -142
  106. data/MultiMarkdown-4/toc.h +0 -15
  107. data/MultiMarkdown-4/transclude.c +0 -307
  108. data/MultiMarkdown-4/transclude.h +0 -28
  109. data/MultiMarkdown-4/writer.c +0 -731
  110. data/MultiMarkdown-4/writer.h +0 -38
@@ -0,0 +1,233 @@
1
+ /**
2
+
3
+ Parser-Template -- Boilerplate parser example using re2c lexer and lemon parser.
4
+
5
+ @file token.h
6
+
7
+ @brief Structure and functions to manage tokens representing portions of a
8
+ text string.
9
+
10
+
11
+ @author Fletcher T. Penney
12
+
13
+ @bug
14
+
15
+ **/
16
+
17
+ /*
18
+
19
+ Copyright © 2016 - 2017 Fletcher T. Penney.
20
+
21
+
22
+ The `MultiMarkdown 6` project is released under the MIT License..
23
+
24
+ GLibFacade.c and GLibFacade.h are from the MultiMarkdown v4 project:
25
+
26
+ https://github.com/fletcher/MultiMarkdown-4/
27
+
28
+ MMD 4 is released under both the MIT License and GPL.
29
+
30
+
31
+ CuTest is released under the zlib/libpng license. See CuTest.c for the text
32
+ of the license.
33
+
34
+
35
+ ## The MIT License ##
36
+
37
+ Permission is hereby granted, free of charge, to any person obtaining a copy
38
+ of this software and associated documentation files (the "Software"), to deal
39
+ in the Software without restriction, including without limitation the rights
40
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
41
+ copies of the Software, and to permit persons to whom the Software is
42
+ furnished to do so, subject to the following conditions:
43
+
44
+ The above copyright notice and this permission notice shall be included in
45
+ all copies or substantial portions of the Software.
46
+
47
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
48
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
49
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
50
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
51
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
52
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
53
+ THE SOFTWARE.
54
+
55
+ */
56
+
57
+ #ifndef TOKEN_PARSER_TEMPLATE_H
58
+ #define TOKEN_PARSER_TEMPLATE_H
59
+
60
+
61
+ #define kUseObjectPoolDisabled 1 //!< Use an object pool to allocate tokens to improve
62
+ //!< performance in memory allocation. Frees all
63
+ //!< tokens at once, however, at end of parsing.
64
+
65
+ /// Should call init() once per thread/use, and drain() once per thread/use.
66
+ /// This allows us to know when the pool is no longer being used and it is safe
67
+ /// to free.
68
+
69
+ #ifdef kUseObjectPool
70
+ void token_pool_init(void); //!< Initialize object pool for allocating tokens
71
+ void token_pool_drain(void); //!< Drain pool to free memory when parse complete
72
+ void token_pool_free(void); //!< Free the token object pool
73
+ #endif
74
+
75
+
76
+ /// Definition for token node struct. This can be used to match an
77
+ /// abstract syntax tree with the appropriate spans in the original
78
+ /// source string.
79
+ struct token {
80
+ unsigned short type; //!< Type for the token
81
+ short can_open; //!< Can token open a matched pair?
82
+ short can_close; //!< Can token close a matched pair?
83
+ short unmatched; //!< Has token been matched yet?
84
+
85
+ size_t start; //!< Starting offset in the source string
86
+ size_t len; //!< Length of the token in the source string
87
+
88
+ struct token * next; //!< Pointer to next token in the chain
89
+ struct token * prev; //!< Pointer to previous marker in the chain
90
+ struct token * child; //!< Pointer to child chain
91
+
92
+ struct token * tail; //!< Pointer to last token in the chain
93
+
94
+ struct token * mate; //!< Pointer to other token in matched pair
95
+ };
96
+
97
+ typedef struct token token;
98
+
99
+
100
+ /// Get pointer to a new token
101
+ token * token_new(
102
+ unsigned short type, //!< Type for new token
103
+ size_t start, //!< Starting offset for token
104
+ size_t len //!< Len of token
105
+ );
106
+
107
+ /// Duplicate an existing token
108
+ token * token_copy(
109
+ token * original //!< Token to be copied
110
+ );
111
+
112
+ /// Create a parent for a chain of tokens
113
+ token * token_new_parent(
114
+ token * child, //!< Pointer to child token chain
115
+ unsigned short type //!< Type for new token
116
+ );
117
+
118
+ /// Add a new token to the end of a token chain. The new token
119
+ /// may or may not also be the start of a chain
120
+ void token_chain_append(
121
+ token * chain_start, //!< Pointer to start of token chain
122
+ token * t //!< Pointer to token to append
123
+ );
124
+
125
+ /// Add a new token to the end of a parent's child
126
+ /// token chain. The new token may or may not be
127
+ /// the start of a chain.
128
+ void token_append_child(
129
+ token * parent, //!< Pointer to parent node
130
+ token * t //!< Pointer to token to append
131
+ );
132
+
133
+ /// Remove the first child of a token
134
+ void token_remove_first_child(
135
+ token * parent //!< Pointer to parent node
136
+ );
137
+
138
+ /// Remove the last child of a token
139
+ void token_remove_last_child(
140
+ token * parent //!< Pointer to parent node
141
+ );
142
+
143
+ /// Remove the last token in a chain
144
+ void token_remove_tail(token * head);
145
+
146
+ /// Pop token out of it's chain, connecting head and tail of chain back together.
147
+ /// Token must be freed if it is no longer needed.
148
+ void token_pop_link_from_chain(
149
+ token * t //!< Pointer to token to remove
150
+ );
151
+
152
+ /// Remove one or more tokens from chain
153
+ void tokens_prune(
154
+ token * first, //!< Pointer to first node to be removed
155
+ token * last //!< Pointer to last node to be removed
156
+ );
157
+
158
+ /// Given a start/stop point in token chain, create a new parent token.
159
+ /// Reinsert the new parent in place of the removed segment.
160
+ /// Return pointer to new container token.
161
+ token * token_prune_graft(
162
+ token * first, //!< Pointer to first node to be removed
163
+ token * last, //!< Pointer to last node to be removed
164
+ unsigned short container_type //!< Type for new parent node for removed section
165
+ );
166
+
167
+ /// Free token
168
+ void token_free(
169
+ token * t //!< Pointer to token to be freed
170
+ );
171
+
172
+ /// Free token tree
173
+ void token_tree_free(
174
+ token * t //!< Pointer to token to be freed
175
+ );
176
+
177
+ /// Print a description of the token based on specified string
178
+ void token_describe(
179
+ token * t, //!< Pointer to token to described
180
+ const char * string //!< Source string
181
+ );
182
+
183
+ /// Print a description of the token tree based on specified string
184
+ void token_tree_describe(
185
+ token * t, //!< Pointer to token to described
186
+ const char * string //!< Source string
187
+ );
188
+
189
+ /// Find the child node of a given parent that contains the specified
190
+ /// offset position.
191
+ token * token_child_for_offset(
192
+ token * parent, //!< Pointer to parent token
193
+ size_t offset //!< Search position
194
+ );
195
+
196
+ /// Find first child node of a given parent that intersects the specified
197
+ /// offset range.
198
+ token * token_first_child_in_range(
199
+ token * parent, //!< Pointer to parent token
200
+ size_t start, //!< Start search position
201
+ size_t len //!< Search length
202
+ );
203
+
204
+ /// Find last child node of a given parent that intersects the specified
205
+ /// offset range.
206
+ token * token_last_child_in_range(
207
+ token * parent, //!< Pointer to parent token
208
+ size_t start, //!< Start search position
209
+ size_t len //!< Search length
210
+ );
211
+
212
+ void token_trim_leading_whitespace(token * t, const char * string);
213
+
214
+ void token_trim_trailing_whitespace(token * t, const char * string);
215
+
216
+ void token_trim_whitespace(token * t, const char * string);
217
+
218
+
219
+ ///
220
+ token * token_chain_accept(token ** t, short type);
221
+
222
+ token * token_chain_accept_multiple(token ** t, int n, ...);
223
+
224
+ void token_skip_until_type(token ** t, short type);
225
+
226
+ void token_skip_until_type_multiple(token ** t, int n, ...);
227
+
228
+ void token_split_on_char(token * t, const char * source, const char c);
229
+
230
+ void token_split(token * t, size_t start, size_t len, unsigned short new_type);
231
+
232
+ #endif
233
+
data/ext/mmd/latex.c ADDED
@@ -0,0 +1,2435 @@
1
+ /**
2
+
3
+ MultiMarkdown 6 -- Lightweight markup processor to produce HTML, LaTeX, and more.
4
+
5
+ @file latex.c
6
+
7
+ @brief Convert token tree to LaTeX output.
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 text
30
+ of the license.
31
+
32
+
33
+ ## The MIT License ##
34
+
35
+ Permission is hereby granted, free of charge, to any person obtaining a copy
36
+ of this software and associated documentation files (the "Software"), to deal
37
+ in the Software without restriction, including without limitation the rights
38
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
39
+ copies of the Software, and to permit persons to whom the Software is
40
+ furnished to do so, subject to the following conditions:
41
+
42
+ The above copyright notice and this permission notice shall be included in
43
+ all copies or substantial portions of the Software.
44
+
45
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
46
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
47
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
48
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
49
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
50
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
51
+ THE SOFTWARE.
52
+
53
+ */
54
+
55
+ #include <ctype.h>
56
+ #include <stdio.h>
57
+ #include <stdlib.h>
58
+ #include <string.h>
59
+
60
+ #include "char.h"
61
+ #include "i18n.h"
62
+ #include "latex.h"
63
+ #include "parser.h"
64
+ #include "scanners.h"
65
+
66
+ #define print(x) d_string_append(out, x)
67
+ #define print_const(x) d_string_append_c_array(out, x, sizeof(x) - 1)
68
+ #define print_char(x) d_string_append_c(out, x)
69
+ #define printf(...) d_string_append_printf(out, __VA_ARGS__)
70
+ #define print_token(t) d_string_append_c_array(out, &(source[t->start]), t->len)
71
+ #define print_localized(x) mmd_print_localized_char_latex(out, x, scratch)
72
+
73
+
74
+ /// strdup() not available on all platforms
75
+ static char * my_strdup(const char * source) {
76
+ if (source == NULL) {
77
+ return NULL;
78
+ }
79
+
80
+ char * result = malloc(strlen(source) + 1);
81
+
82
+ if (result) {
83
+ strcpy(result, source);
84
+ }
85
+
86
+ return result;
87
+ }
88
+
89
+
90
+ void mmd_print_char_latex(DString * out, char c) {
91
+ switch (c) {
92
+ case '\\':
93
+ print_const("\\textbackslash{}");
94
+ break;
95
+
96
+ case '~':
97
+ print_const("\\ensuremath{\\sim}");
98
+ break;
99
+
100
+ case '/':
101
+ print_const("\\slash ");
102
+ break;
103
+
104
+ case '^':
105
+ print_const("\\^{}");
106
+ break;
107
+
108
+ case '<':
109
+ case '>':
110
+ print_char('$');
111
+ print_char(c);
112
+ print_char('$');
113
+ break;
114
+
115
+ case '|':
116
+ print_const("\\textbar{}");
117
+ break;
118
+
119
+ case '#':
120
+ case '{':
121
+ case '}':
122
+ case '$':
123
+ case '%':
124
+ case '&':
125
+ case '_':
126
+ print_char('\\');
127
+
128
+ default:
129
+ print_char(c);
130
+ break;
131
+ }
132
+ }
133
+
134
+
135
+ void mmd_print_string_latex(DString * out, const char * str) {
136
+ if (str == NULL) {
137
+ return;
138
+ }
139
+
140
+ while (*str != '\0') {
141
+ mmd_print_char_latex(out, *str);
142
+ str++;
143
+ }
144
+ }
145
+
146
+
147
+ void mmd_print_localized_char_latex(DString * out, unsigned short type, scratch_pad * scratch) {
148
+ switch (type) {
149
+ case DASH_N:
150
+ print_const("--");
151
+ break;
152
+
153
+ case DASH_M:
154
+ print_const("---");
155
+ break;
156
+
157
+ case ELLIPSIS:
158
+ print_const("{\\ldots}");
159
+ break;
160
+
161
+ case APOSTROPHE:
162
+ print_const("'");
163
+ break;
164
+
165
+ case QUOTE_LEFT_SINGLE:
166
+ switch (scratch->quotes_lang) {
167
+ case SWEDISH:
168
+ print_const("'");
169
+ break;
170
+
171
+ case FRENCH:
172
+ print_const("'");
173
+ break;
174
+
175
+ case GERMAN:
176
+ print_const("‚");
177
+ break;
178
+
179
+ case GERMANGUILL:
180
+ print_const("›");
181
+ break;
182
+
183
+ default:
184
+ print_const("`");
185
+ }
186
+
187
+ break;
188
+
189
+ case QUOTE_RIGHT_SINGLE:
190
+ switch (scratch->quotes_lang) {
191
+ case GERMAN:
192
+ print_const("`");
193
+ break;
194
+
195
+ case GERMANGUILL:
196
+ print_const("‹");
197
+ break;
198
+
199
+ default:
200
+ print_const("'");
201
+ }
202
+
203
+ break;
204
+
205
+ case QUOTE_LEFT_DOUBLE:
206
+ switch (scratch->quotes_lang) {
207
+ case DUTCH:
208
+ case GERMAN:
209
+ print_const("„");
210
+ break;
211
+
212
+ case GERMANGUILL:
213
+ print_const("»");
214
+ break;
215
+
216
+ case FRENCH:
217
+ print_const("«");
218
+ break;
219
+
220
+ case SWEDISH:
221
+ print_const("''");
222
+ break;
223
+
224
+ default:
225
+ print_const("``");
226
+ }
227
+
228
+ break;
229
+
230
+ case QUOTE_RIGHT_DOUBLE:
231
+ switch (scratch->quotes_lang) {
232
+ case GERMAN:
233
+ print_const("``");
234
+ break;
235
+
236
+ case GERMANGUILL:
237
+ print_const("«");
238
+ break;
239
+
240
+ case FRENCH:
241
+ print_const("»");
242
+ break;
243
+
244
+ case SWEDISH:
245
+ case DUTCH:
246
+ default:
247
+ print_const("''");
248
+ }
249
+
250
+ break;
251
+ }
252
+ }
253
+
254
+
255
+ void mmd_export_link_latex(DString * out, const char * source, token * text, link * link, scratch_pad * scratch) {
256
+ char * temp_char;
257
+
258
+ if (link->url) {
259
+ if (link->url[0] == '#') {
260
+ // Internal link
261
+ if (text && text->child) {
262
+ temp_char = label_from_token(source, text);
263
+
264
+ if (strcmp(temp_char, &(link->url[1])) == 0) {
265
+ // [bar][bar] or [bar](#bar) or [bar]
266
+ printf("\\autoref{%s}", &(link->url)[1]);
267
+ } else {
268
+ mmd_export_token_tree_latex(out, source, text->child, scratch);
269
+ print_const(" (");
270
+ printf("\\autoref{%s}", &(link->url)[1]);
271
+ print_const(")");
272
+ }
273
+
274
+ free(temp_char);
275
+ } else {
276
+ printf("\\autoref{%s}", &(link->url)[1]);
277
+ }
278
+
279
+ return;
280
+ } else {
281
+ printf("\\href{%s}", link->url);
282
+ }
283
+ } else {
284
+ print_const("\\href{}");
285
+ }
286
+
287
+ print_const("{");
288
+
289
+ // If we're printing contents of bracket as text, then ensure we include it all
290
+ if (text && text->child && text->child->len > 1) {
291
+ text->child->next->start--;
292
+ text->child->next->len++;
293
+ }
294
+
295
+ mmd_export_token_tree_latex(out, source, text->child, scratch);
296
+
297
+ print_const("}");
298
+
299
+ // Reprint as footnote for printed copies
300
+ printf("\\footnote{\\href{%s}{", link->url);
301
+ mmd_print_string_latex(out, link->url);
302
+ print_const("}}");
303
+ }
304
+
305
+
306
+ static char * correct_dimension_units(char *original) {
307
+ char *result;
308
+ int i;
309
+
310
+ result = my_strdup(original);
311
+
312
+ for (i = 0; result[i]; i++) {
313
+ result[i] = tolower(result[i]);
314
+ }
315
+
316
+ if (strstr(&result[strlen(result) - 2], "px")) {
317
+ result[strlen(result) - 2] = '\0';
318
+ strcat(result, "pt");
319
+ }
320
+
321
+ return result;
322
+ }
323
+
324
+
325
+ void mmd_export_image_latex(DString * out, const char * source, token * text, link * link, scratch_pad * scratch, bool is_figure) {
326
+ attr * a = link->attributes;
327
+ char * height = NULL;
328
+ char * width = NULL;
329
+ float temp_float;
330
+
331
+ // Compatibility mode doesn't allow figures
332
+ if (scratch->extensions & EXT_COMPATIBILITY) {
333
+ is_figure = false;
334
+ }
335
+
336
+ if (is_figure) {
337
+ print_const("\\begin{figure}[htbp]\n\\centering\n");
338
+ scratch->close_para = false;
339
+ }
340
+
341
+ // Check attributes for dimensions
342
+ while (a) {
343
+ if (strcmp("height", a->key) == 0) {
344
+ height = correct_dimension_units(a->value);
345
+ } else if (strcmp("width", a->key) == 0) {
346
+ width = correct_dimension_units(a->value);
347
+ }
348
+
349
+ a = a->next;
350
+ }
351
+
352
+ print_const("\\includegraphics[");
353
+
354
+ if (height || width) {
355
+ if (!height || !width) {
356
+ // One not specified, preserve aspect
357
+ print_const("keepaspectratio,");
358
+ }
359
+
360
+ if (width) {
361
+ // Width specified
362
+ if (width[strlen(width) - 1] == '%') {
363
+ // specified as percent
364
+ width[strlen(width) - 1] = '\0';
365
+ temp_float = strtod(width, NULL);
366
+ temp_float = temp_float / 100.0f;
367
+ printf("width=%.4f\\textwidth,", temp_float);
368
+ } else {
369
+ printf("width=%s,", width);
370
+ }
371
+
372
+ free(width);
373
+ } else {
374
+ // Default width
375
+ print_const("width=\\textwidth,");
376
+ }
377
+
378
+ if (height) {
379
+ // Height specified
380
+ if (height[strlen(height) - 1] == '%') {
381
+ // specified as percent
382
+ height[strlen(height) - 1] = '\0';
383
+ temp_float = strtod(height, NULL);
384
+ temp_float = temp_float / 100.0f;
385
+ printf("height=%.4f\\textheight", temp_float);
386
+ } else {
387
+ printf("height=%s", height);
388
+ }
389
+
390
+ free(height);
391
+ } else {
392
+ // Default height
393
+ print_const("height=0.75\\textheight");
394
+ }
395
+ } else {
396
+ // no dimensions specified, use sensible defaults
397
+ print_const("keepaspectratio,width=\\textwidth,height=0.75\\textheight");
398
+ }
399
+
400
+ if (link->url) {
401
+ printf("]{%s}", link->url);
402
+ } else {
403
+ print_const("]{}");
404
+ }
405
+
406
+
407
+ if (is_figure) {
408
+ print_const("\n");
409
+
410
+ if (text) {
411
+ print_const("\\caption{");
412
+ mmd_export_token_tree_latex(out, source, text->child, scratch);
413
+ print_const("}\n");
414
+ }
415
+
416
+ if (link->label) {
417
+ // \todo: Need to decide on approach to id's
418
+ char * label = label_from_token(source, link->label);
419
+ printf("\\label{%s}\n", label);
420
+ free(label);
421
+ }
422
+
423
+ print_const("\\end{figure}");
424
+ }
425
+ }
426
+
427
+
428
+ void mmd_export_toc_entry_latex(DString * out, const char * source, scratch_pad * scratch, size_t * counter, short level) {
429
+ token * entry, * next;
430
+ short entry_level, next_level;
431
+ char * temp_char;
432
+
433
+ print_const("\\begin{itemize}\n\n");
434
+
435
+ // Iterate over tokens
436
+ while (*counter < scratch->header_stack->size) {
437
+ // Get token for header
438
+ entry = stack_peek_index(scratch->header_stack, *counter);
439
+ entry_level = raw_level_for_header(entry);
440
+
441
+ if (entry_level >= level) {
442
+ // This entry is a direct descendant of the parent
443
+ temp_char = label_from_header(source, entry);
444
+ print_const("\\item{} ");
445
+ mmd_export_token_tree_latex(out, source, entry->child, scratch);
446
+ printf("(\\autoref{%s})\n\n", temp_char);
447
+
448
+ if (*counter < scratch->header_stack->size - 1) {
449
+ next = stack_peek_index(scratch->header_stack, *counter + 1);
450
+ next_level = next->type - BLOCK_H1 + 1;
451
+
452
+ if (next_level > entry_level) {
453
+ // This entry has children
454
+ (*counter)++;
455
+ mmd_export_toc_entry_latex(out, source, scratch, counter, entry_level + 1);
456
+ }
457
+ }
458
+
459
+ free(temp_char);
460
+ } else if (entry_level < level ) {
461
+ // If entry < level, exit this level
462
+ // Decrement counter first, so that we can test it again later
463
+ (*counter)--;
464
+ break;
465
+ }
466
+
467
+ // Increment counter
468
+ (*counter)++;
469
+ }
470
+
471
+ print_const("\\end{itemize}\n\n");
472
+ }
473
+
474
+
475
+ void mmd_export_toc_latex(DString * out, const char * source, scratch_pad * scratch) {
476
+ size_t counter = 0;
477
+
478
+ mmd_export_toc_entry_latex(out, source, scratch, &counter, 0);
479
+ }
480
+
481
+
482
+ void mmd_export_token_latex(DString * out, const char * source, token * t, scratch_pad * scratch) {
483
+ if (t == NULL) {
484
+ return;
485
+ }
486
+
487
+ short temp_short;
488
+ short temp_short2;
489
+ short temp_short3;
490
+ link * temp_link = NULL;
491
+ char * temp_char = NULL;
492
+ char * temp_char2 = NULL;
493
+ char * temp_char3 = NULL;
494
+ bool temp_bool = 0;
495
+ token * temp_token = NULL;
496
+ footnote * temp_note = NULL;
497
+
498
+ switch (t->type) {
499
+ case AMPERSAND:
500
+ case AMPERSAND_LONG:
501
+ print_const("\\&");
502
+ break;
503
+
504
+ case ANGLE_LEFT:
505
+ print_const("<");
506
+ break;
507
+
508
+ case ANGLE_RIGHT:
509
+ print_const(">");
510
+ break;
511
+
512
+ case APOSTROPHE:
513
+ if (!(scratch->extensions & EXT_SMART)) {
514
+ print_token(t);
515
+ } else {
516
+ print_localized(APOSTROPHE);
517
+ }
518
+
519
+ break;
520
+
521
+ case BACKTICK:
522
+ print_token(t);
523
+ break;
524
+
525
+ case BLOCK_BLOCKQUOTE:
526
+ pad(out, 2, scratch);
527
+ print_const("\\begin{quote}\n");
528
+ scratch->padded = 2;
529
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
530
+ pad(out, 1, scratch);
531
+ print_const("\\end{quote}");
532
+ scratch->padded = 0;
533
+ break;
534
+
535
+ case BLOCK_CODE_FENCED:
536
+ pad(out, 2, scratch);
537
+
538
+ temp_char = get_fence_language_specifier(t->child->child, source);
539
+
540
+ if (temp_char) {
541
+ if (strncmp("{=", temp_char, 2) == 0) {
542
+ // Raw source
543
+ if (raw_filter_text_matches(temp_char, FORMAT_LATEX)) {
544
+ switch (t->child->tail->type) {
545
+ case LINE_FENCE_BACKTICK_3:
546
+ case LINE_FENCE_BACKTICK_4:
547
+ case LINE_FENCE_BACKTICK_5:
548
+ temp_token = t->child->tail;
549
+ break;
550
+
551
+ default:
552
+ temp_token = NULL;
553
+ }
554
+
555
+ if (temp_token) {
556
+ d_string_append_c_array(out, &source[t->child->next->start], temp_token->start - t->child->next->start);
557
+ scratch->padded = 1;
558
+ } else {
559
+ d_string_append_c_array(out, &source[t->child->start + t->child->len], t->start + t->len - t->child->next->start);
560
+ scratch->padded = 0;
561
+ }
562
+ }
563
+
564
+ free(temp_char);
565
+ break;
566
+ }
567
+
568
+ printf("\\begin{lstlisting}[language=%s]\n", temp_char);
569
+ } else {
570
+ print_const("\\begin{verbatim}\n");
571
+ }
572
+
573
+ mmd_export_token_tree_latex_raw(out, source, t->child->next, scratch);
574
+
575
+ if (temp_char) {
576
+ print_const("\\end{lstlisting}");
577
+ free(temp_char);
578
+ } else {
579
+ print_const("\\end{verbatim}");
580
+ }
581
+
582
+ scratch->padded = 0;
583
+ break;
584
+
585
+ case BLOCK_CODE_INDENTED:
586
+ pad(out, 2, scratch);
587
+ print_const("\\begin{verbatim}\n");
588
+ mmd_export_token_tree_latex_raw(out, source, t->child, scratch);
589
+ print_const("\\end{verbatim}");
590
+ scratch->padded = 0;
591
+ break;
592
+
593
+ case BLOCK_DEFINITION:
594
+ pad(out, 2, scratch);
595
+
596
+ temp_short = scratch->list_is_tight;
597
+
598
+ if (!(t->child->next && (t->child->next->type == BLOCK_EMPTY) && t->child->next->next)) {
599
+ scratch->list_is_tight = true;
600
+ }
601
+
602
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
603
+ scratch->padded = 0;
604
+
605
+ scratch->list_is_tight = temp_short;
606
+ break;
607
+
608
+ case BLOCK_DEFLIST:
609
+ pad(out, 2, scratch);
610
+
611
+ // Group consecutive definition lists into a single list.
612
+ // lemon's LALR(1) parser can't properly handle this (to my understanding).
613
+
614
+ if (!(t->prev && (t->prev->type == BLOCK_DEFLIST))) {
615
+ print_const("\\begin{description}\n");
616
+ }
617
+
618
+ scratch->padded = 2;
619
+
620
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
621
+ pad(out, 1, scratch);
622
+
623
+ if (!(t->next && (t->next->type == BLOCK_DEFLIST))) {
624
+ print_const("\\end{description}\n");
625
+ }
626
+
627
+ scratch->padded = 1;
628
+ break;
629
+
630
+ case BLOCK_EMPTY:
631
+ break;
632
+
633
+ case BLOCK_H1:
634
+ case BLOCK_H2:
635
+ case BLOCK_H3:
636
+ case BLOCK_H4:
637
+ case BLOCK_H5:
638
+ case BLOCK_H6:
639
+ case BLOCK_SETEXT_1:
640
+ case BLOCK_SETEXT_2:
641
+ pad(out, 2, scratch);
642
+
643
+ switch (t->type) {
644
+ case BLOCK_SETEXT_1:
645
+ temp_short = 1;
646
+ break;
647
+
648
+ case BLOCK_SETEXT_2:
649
+ temp_short = 2;
650
+ break;
651
+
652
+ default:
653
+ temp_short = t->type - BLOCK_H1 + 1;
654
+ }
655
+
656
+ switch (temp_short + scratch->base_header_level - 1) {
657
+ case 1:
658
+ print_const("\\part{");
659
+ break;
660
+
661
+ case 2:
662
+ print_const("\\chapter{");
663
+ break;
664
+
665
+ case 3:
666
+ print_const("\\section{");
667
+ break;
668
+
669
+ case 4:
670
+ print_const("\\subsection{");
671
+ break;
672
+
673
+ case 5:
674
+ print_const("\\subsubsection{");
675
+ break;
676
+
677
+ case 6:
678
+ print_const("\\paragraph{");
679
+ break;
680
+
681
+ case 7:
682
+ print_const("\\subparagraph{");
683
+ break;
684
+ }
685
+
686
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
687
+
688
+ if (scratch->extensions & EXT_NO_LABELS) {
689
+ print_const("}");
690
+ } else {
691
+ temp_token = manual_label_from_header(t, source);
692
+
693
+ if (temp_token) {
694
+ temp_char = label_from_token(source, temp_token);
695
+ } else {
696
+ temp_char = label_from_token(source, t);
697
+ }
698
+
699
+ printf("}\n\\label{%s}", temp_char);
700
+ free(temp_char);
701
+ }
702
+
703
+ scratch->padded = 0;
704
+ break;
705
+
706
+ case BLOCK_HR:
707
+ pad(out, 2, scratch);
708
+ print_const("\\begin{center}\\rule{3in}{0.4pt}\\end{center}");
709
+ scratch->padded = 0;
710
+ break;
711
+
712
+ case BLOCK_HTML:
713
+ // Don't print HTML
714
+ break;
715
+
716
+ case BLOCK_LIST_BULLETED_LOOSE:
717
+ case BLOCK_LIST_BULLETED:
718
+ temp_short = scratch->list_is_tight;
719
+
720
+ switch (t->type) {
721
+ case BLOCK_LIST_BULLETED_LOOSE:
722
+ scratch->list_is_tight = false;
723
+ break;
724
+
725
+ case BLOCK_LIST_BULLETED:
726
+ scratch->list_is_tight = true;
727
+ break;
728
+ }
729
+
730
+ pad(out, 2, scratch);
731
+ print_const("\\begin{itemize}");
732
+ scratch->padded = 1;
733
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
734
+ pad(out, 2, scratch);
735
+ print_const("\\end{itemize}");
736
+ scratch->padded = 0;
737
+ scratch->list_is_tight = temp_short;
738
+ break;
739
+
740
+ case BLOCK_LIST_ENUMERATED_LOOSE:
741
+ case BLOCK_LIST_ENUMERATED:
742
+ temp_short = scratch->list_is_tight;
743
+
744
+ switch (t->type) {
745
+ case BLOCK_LIST_ENUMERATED_LOOSE:
746
+ scratch->list_is_tight = false;
747
+ break;
748
+
749
+ case BLOCK_LIST_ENUMERATED:
750
+ scratch->list_is_tight = true;
751
+ break;
752
+ }
753
+
754
+ pad(out, 2, scratch);
755
+ print_const("\\begin{enumerate}");
756
+ scratch->padded = 1;
757
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
758
+ pad(out, 2, scratch);
759
+ print_const("\\end{enumerate}");
760
+ scratch->padded = 0;
761
+ scratch->list_is_tight = temp_short;
762
+ break;
763
+
764
+ case BLOCK_LIST_ITEM:
765
+ pad(out, 2, scratch);
766
+ print_const("\\item{} ");
767
+ scratch->padded = 2;
768
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
769
+ scratch->padded = 0;
770
+ break;
771
+
772
+ case BLOCK_LIST_ITEM_TIGHT:
773
+ pad(out, 2, scratch);
774
+ print_const("\\item{} ");
775
+ scratch->padded = 2;
776
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
777
+ scratch->padded = 0;
778
+ break;
779
+
780
+ case BLOCK_META:
781
+ break;
782
+
783
+ case BLOCK_PARA:
784
+ pad(out, 2, scratch);
785
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
786
+ scratch->padded = 0;
787
+ break;
788
+
789
+ case BLOCK_TABLE:
790
+ pad(out, 2, scratch);
791
+
792
+ print_const("\\begin{table}[htbp]\n");
793
+
794
+ print_const("\\begin{minipage}{\\linewidth}\n\\setlength{\\tymax}{0.5\\linewidth}\n\\centering\n\\small\n");
795
+
796
+ // Are we followed by a caption?
797
+ if (table_has_caption(t)) {
798
+ temp_token = t->next->child;
799
+
800
+ if (temp_token->next &&
801
+ temp_token->next->type == PAIR_BRACKET) {
802
+ temp_token = temp_token->next;
803
+ }
804
+
805
+ temp_char = label_from_token(source, temp_token);
806
+
807
+ t->next->child->child->type = TEXT_EMPTY;
808
+ t->next->child->child->mate->type = TEXT_EMPTY;
809
+
810
+ print_const("\\caption{");
811
+ mmd_export_token_tree_latex(out, source, t->next->child->child, scratch);
812
+ print_const("}\n");
813
+
814
+ printf("\\label{%s}\n", temp_char);
815
+ free(temp_char);
816
+
817
+ temp_short = 1;
818
+ } else {
819
+ temp_short = 0;
820
+ }
821
+
822
+ read_table_column_alignments(source, t, scratch);
823
+
824
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
825
+ pad(out, 1, scratch);
826
+ print_const("\n\\end{tabulary}\n\\end{minipage}");
827
+
828
+ print_const("\n\\end{table}");
829
+
830
+ scratch->skip_token = temp_short;
831
+ scratch->padded = 0;
832
+ break;
833
+
834
+ case BLOCK_TABLE_HEADER:
835
+ pad(out, 2, scratch);
836
+
837
+ print_const("\\begin{tabulary}{\\textwidth}{@{}");
838
+
839
+ for (int i = 0; i < scratch->table_column_count; ++i) {
840
+ switch (scratch->table_alignment[i]) {
841
+ case 'l':
842
+ case 'L':
843
+ case 'r':
844
+ case 'R':
845
+ case 'c':
846
+ case 'C':
847
+ print_char(scratch->table_alignment[i]);
848
+ break;
849
+
850
+ case 'N':
851
+ print_char('L');
852
+ break;
853
+
854
+ default:
855
+ print_char('l');
856
+ break;
857
+ }
858
+ }
859
+
860
+ print_const("@{}} \\toprule\n");
861
+
862
+ scratch->in_table_header = 1;
863
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
864
+ scratch->in_table_header = 0;
865
+
866
+ print_const("\\midrule\n");
867
+
868
+ scratch->padded = 1;
869
+ break;
870
+
871
+ case BLOCK_TABLE_SECTION:
872
+ pad(out, 2, scratch);
873
+ scratch->padded = 2;
874
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
875
+ print_const("\\bottomrule");
876
+ scratch->padded = 0;
877
+ break;
878
+
879
+ case BLOCK_TERM:
880
+ pad(out, 2, scratch);
881
+ print_const("\\item[");
882
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
883
+ print_const("]");
884
+ scratch->padded = 0;
885
+ break;
886
+
887
+ case BLOCK_TOC:
888
+ pad(out, 2, scratch);
889
+ print_const("\\tableofcontents");
890
+ scratch->padded = 0;
891
+ break;
892
+
893
+ case BRACE_DOUBLE_LEFT:
894
+ print_const("\\{\\{");
895
+ break;
896
+
897
+ case BRACE_DOUBLE_RIGHT:
898
+ print_const("\\}\\}");
899
+ break;
900
+
901
+ case BRACKET_LEFT:
902
+ print_const("[");
903
+ break;
904
+
905
+ case BRACKET_ABBREVIATION_LEFT:
906
+ print_const("[>");
907
+ break;
908
+
909
+ case BRACKET_CITATION_LEFT:
910
+ print_const("[#");
911
+ break;
912
+
913
+ case BRACKET_FOOTNOTE_LEFT:
914
+ print_const("[^");
915
+ break;
916
+
917
+ case BRACKET_GLOSSARY_LEFT:
918
+ print_const("[?");
919
+ break;
920
+
921
+ case BRACKET_IMAGE_LEFT:
922
+ print_const("![");
923
+ break;
924
+
925
+ case BRACKET_VARIABLE_LEFT:
926
+ print_const("[\%");
927
+ break;
928
+
929
+ case BRACKET_RIGHT:
930
+ print_const("]");
931
+ break;
932
+
933
+ case CODE_FENCE:
934
+ break;
935
+
936
+ case COLON:
937
+ print_const(":");
938
+ break;
939
+
940
+ case CRITIC_ADD_OPEN:
941
+ print_const("\\{++");
942
+ break;
943
+
944
+ case CRITIC_ADD_CLOSE:
945
+ print_const("++\\}");
946
+ break;
947
+
948
+ case CRITIC_COM_OPEN:
949
+ print_const("\\{>>");
950
+ break;
951
+
952
+ case CRITIC_COM_CLOSE:
953
+ print_const("<<\\}");
954
+ break;
955
+
956
+ case CRITIC_DEL_OPEN:
957
+ print_const("\\{--");
958
+ break;
959
+
960
+ case CRITIC_DEL_CLOSE:
961
+ print_const("--\\}");
962
+ break;
963
+
964
+ case CRITIC_HI_OPEN:
965
+ print_const("\\{==");
966
+ break;
967
+
968
+ case CRITIC_HI_CLOSE:
969
+ print_const("==\\}");
970
+ break;
971
+
972
+ case CRITIC_SUB_OPEN:
973
+ print_const("\\{~~");
974
+ break;
975
+
976
+ case CRITIC_SUB_DIV:
977
+ print_const("~>");
978
+ break;
979
+
980
+ case CRITIC_SUB_CLOSE:
981
+ print_const("~~\\}");
982
+ break;
983
+
984
+ case DASH_M:
985
+ if (!(scratch->extensions & EXT_SMART)) {
986
+ print_token(t);
987
+ } else {
988
+ print_localized(DASH_M);
989
+ }
990
+
991
+ break;
992
+
993
+ case DASH_N:
994
+ if (!(scratch->extensions & EXT_SMART)) {
995
+ print_token(t);
996
+ } else {
997
+ print_localized(DASH_N);
998
+ }
999
+
1000
+ break;
1001
+
1002
+ case DOC_START_TOKEN:
1003
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1004
+ break;
1005
+
1006
+ case ELLIPSIS:
1007
+ if (!(scratch->extensions & EXT_SMART)) {
1008
+ print_token(t);
1009
+ } else {
1010
+ print_localized(ELLIPSIS);
1011
+ }
1012
+
1013
+ break;
1014
+
1015
+ case EMPH_START:
1016
+ print_const("\\emph{");
1017
+ break;
1018
+
1019
+ case EMPH_STOP:
1020
+ print_const("}");
1021
+ break;
1022
+
1023
+ case EQUAL:
1024
+ print_const("=");
1025
+ break;
1026
+
1027
+ case ESCAPED_CHARACTER:
1028
+ if (!(scratch->extensions & EXT_COMPATIBILITY) &&
1029
+ (source[t->start + 1] == ' ')) {
1030
+ print_const("~");
1031
+ } else {
1032
+ mmd_print_char_latex(out, source[t->start + 1]);
1033
+ }
1034
+
1035
+ break;
1036
+
1037
+ case HASH1:
1038
+ case HASH2:
1039
+ case HASH3:
1040
+ case HASH4:
1041
+ case HASH5:
1042
+ case HASH6:
1043
+ for (int i = 0; i < t->len; ++i) {
1044
+ if (source[t->start + i] == '#') {
1045
+ print_char('\\');
1046
+ print_char('#');
1047
+ } else {
1048
+ mmd_print_char_latex(out, source[t->start + i]);
1049
+ }
1050
+ }
1051
+
1052
+ break;
1053
+
1054
+ case HTML_ENTITY:
1055
+ if (source[t->start + 1] == '#') {
1056
+ print_const("\\&\\#");
1057
+ d_string_append_c_array(out, &(source[t->start + 2]), t->len - 2);
1058
+ } else {
1059
+ print_const("\\");
1060
+ print_token(t);
1061
+ }
1062
+
1063
+ break;
1064
+
1065
+ case HTML_COMMENT_START:
1066
+ if (!(scratch->extensions & EXT_SMART)) {
1067
+ print_const("<!--");
1068
+ } else {
1069
+ print_const("<!");
1070
+ print_localized(DASH_N);
1071
+ }
1072
+
1073
+ break;
1074
+
1075
+ case HTML_COMMENT_STOP:
1076
+ if (!(scratch->extensions & EXT_SMART)) {
1077
+ print_const("-->");
1078
+ } else {
1079
+ print_localized(DASH_N);
1080
+ print_const(">");
1081
+ }
1082
+
1083
+ break;
1084
+
1085
+ case INDENT_SPACE:
1086
+ print_char(' ');
1087
+ break;
1088
+
1089
+ case INDENT_TAB:
1090
+ print_char('\t');
1091
+ break;
1092
+
1093
+ case LINE_LIST_BULLETED:
1094
+ case LINE_LIST_ENUMERATED:
1095
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1096
+ break;
1097
+
1098
+ case LINE_SETEXT_2:
1099
+ case MANUAL_LABEL:
1100
+ case MARKER_BLOCKQUOTE:
1101
+ case MARKER_H1:
1102
+ case MARKER_H2:
1103
+ case MARKER_H3:
1104
+ case MARKER_H4:
1105
+ case MARKER_H5:
1106
+ case MARKER_H6:
1107
+ case MARKER_LIST_BULLET:
1108
+ case MARKER_LIST_ENUMERATOR:
1109
+ break;
1110
+
1111
+ case MATH_BRACKET_OPEN:
1112
+ print_const("\\[");
1113
+ break;
1114
+
1115
+ case MATH_BRACKET_CLOSE:
1116
+ print_const("\\]");
1117
+ break;
1118
+
1119
+ case MATH_DOLLAR_SINGLE:
1120
+ if (t->mate) {
1121
+ print_const("$");
1122
+ } else {
1123
+ print_const("\\$");
1124
+ }
1125
+
1126
+ break;
1127
+
1128
+ case MATH_DOLLAR_DOUBLE:
1129
+ if (t->mate) {
1130
+ print_const("$$");
1131
+ } else {
1132
+ print_const("\\$\\$");
1133
+ }
1134
+
1135
+ break;
1136
+
1137
+ case MATH_PAREN_OPEN:
1138
+ print_const("\\(");
1139
+ break;
1140
+
1141
+ case MATH_PAREN_CLOSE:
1142
+ print_const("\\)");
1143
+ break;
1144
+
1145
+ case NON_INDENT_SPACE:
1146
+ print_char(' ');
1147
+ break;
1148
+
1149
+ case PAIR_ANGLE:
1150
+ temp_char = url_accept(source, t->start + 1, t->len - 2, NULL, true);
1151
+
1152
+ if (temp_char) {
1153
+ print_const("\\href{");
1154
+
1155
+ if (scan_email(temp_char)) {
1156
+ if (strncmp("mailto:", temp_char, 7) != 0) {
1157
+ print_const("mailto:");
1158
+ }
1159
+ }
1160
+
1161
+ print(temp_char);
1162
+ print_const("}{");
1163
+ mmd_print_string_latex(out, temp_char);
1164
+ print_const("}");
1165
+ } else if (scan_html(&source[t->start])) {
1166
+ // We ignore HTML blocks
1167
+ if (scan_html_comment(&source[t->start])) {
1168
+ // But allow HTML comments as raw LaTeX
1169
+ d_string_append_c_array(out, &source[t->start + 4], t->len - 4 - 3);
1170
+ }
1171
+ } else {
1172
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1173
+ }
1174
+
1175
+ free(temp_char);
1176
+ break;
1177
+
1178
+ case PAIR_BACKTICK:
1179
+
1180
+ // Strip leading whitespace
1181
+ switch (t->child->next->type) {
1182
+ case TEXT_NL:
1183
+ case INDENT_TAB:
1184
+ case INDENT_SPACE:
1185
+ case NON_INDENT_SPACE:
1186
+ t->child->next->type = TEXT_EMPTY;
1187
+ break;
1188
+
1189
+ case TEXT_PLAIN:
1190
+ while (t->child->next->len && char_is_whitespace(source[t->child->next->start])) {
1191
+ t->child->next->start++;
1192
+ t->child->next->len--;
1193
+ }
1194
+
1195
+ break;
1196
+ }
1197
+
1198
+ // Strip trailing whitespace
1199
+ switch (t->child->mate->prev->type) {
1200
+ case TEXT_NL:
1201
+ case INDENT_TAB:
1202
+ case INDENT_SPACE:
1203
+ case NON_INDENT_SPACE:
1204
+ t->child->mate->prev->type = TEXT_EMPTY;
1205
+ break;
1206
+
1207
+ case TEXT_PLAIN:
1208
+ while (t->child->mate->prev->len && char_is_whitespace(source[t->child->mate->prev->start + t->child->mate->prev->len - 1])) {
1209
+ t->child->mate->prev->len--;
1210
+ }
1211
+
1212
+ break;
1213
+ }
1214
+
1215
+ t->child->type = TEXT_EMPTY;
1216
+ t->child->mate->type = TEXT_EMPTY;
1217
+
1218
+ if (t->next && t->next->type == PAIR_RAW_FILTER) {
1219
+ // Raw text?
1220
+ if (raw_filter_matches(t->next, source, FORMAT_LATEX)) {
1221
+ d_string_append_c_array(out, &(source[t->child->start + t->child->len]), t->child->mate->start - t->child->start - t->child->len);
1222
+ }
1223
+
1224
+ // Skip over PAIR_RAW_FILTER
1225
+ scratch->skip_token = 1;
1226
+ break;
1227
+ }
1228
+
1229
+ print_const("\\texttt{");
1230
+ mmd_export_token_tree_latex_tt(out, source, t->child, scratch);
1231
+ print_const("}");
1232
+ break;
1233
+
1234
+ case PAIR_BRACE:
1235
+ case PAIR_BRACES:
1236
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1237
+ break;
1238
+
1239
+ case PAIR_BRACKET:
1240
+ if ((scratch->extensions & EXT_NOTES) &&
1241
+ (t->next && t->next->type == PAIR_BRACKET_CITATION)) {
1242
+ goto parse_citation;
1243
+ }
1244
+
1245
+ case PAIR_BRACKET_IMAGE:
1246
+ parse_brackets(source, scratch, t, &temp_link, &temp_short, &temp_bool);
1247
+
1248
+ if (temp_link) {
1249
+ if (t->type == PAIR_BRACKET) {
1250
+ // Link
1251
+ mmd_export_link_latex(out, source, t, temp_link, scratch);
1252
+ } else {
1253
+ // Image -- should it be a figure (e.g. image is only thing in paragraph)?
1254
+ temp_token = t->next;
1255
+
1256
+ if (temp_token &&
1257
+ ((temp_token->type == PAIR_BRACKET) ||
1258
+ (temp_token->type == PAIR_PAREN))) {
1259
+ temp_token = temp_token->next;
1260
+ }
1261
+
1262
+ if (temp_token && temp_token->type == TEXT_NL) {
1263
+ temp_token = temp_token->next;
1264
+ }
1265
+
1266
+ if (temp_token && temp_token->type == TEXT_LINEBREAK) {
1267
+ temp_token = temp_token->next;
1268
+ }
1269
+
1270
+ if (t->prev || temp_token) {
1271
+ mmd_export_image_latex(out, source, t, temp_link, scratch, false);
1272
+ } else {
1273
+ mmd_export_image_latex(out, source, t, temp_link, scratch, true);
1274
+ }
1275
+ }
1276
+
1277
+ if (temp_bool) {
1278
+ link_free(temp_link);
1279
+ }
1280
+
1281
+ scratch->skip_token = temp_short;
1282
+
1283
+ return;
1284
+ }
1285
+
1286
+ // No links exist, so treat as normal
1287
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1288
+ break;
1289
+
1290
+ case PAIR_BRACKET_ABBREVIATION:
1291
+
1292
+ // Which might also be an "auto-tagged" abbreviation
1293
+ if (scratch->extensions & EXT_NOTES) {
1294
+ // Note-based syntax enabled
1295
+
1296
+ // Classify this use
1297
+ temp_short3 = scratch->inline_abbreviations_to_free->size;
1298
+ abbreviation_from_bracket(source, scratch, t, &temp_short);
1299
+
1300
+ if (temp_short == -1) {
1301
+ // This instance is not properly formed
1302
+ print_const("[>");
1303
+ mmd_export_token_tree_latex(out, source, t->child->next, scratch);
1304
+ print_const("]");
1305
+ break;
1306
+ }
1307
+
1308
+ // Get instance of the note used
1309
+ temp_note = stack_peek_index(scratch->used_abbreviations, temp_short - 1);
1310
+
1311
+ if (temp_short3 == scratch->inline_abbreviations_to_free->size) {
1312
+ // This is a reference definition
1313
+ printf("\\gls{%s}", temp_note->label_text);
1314
+ } else {
1315
+ // This is an inline definition
1316
+ print_const("\\newacronym{");
1317
+ print(temp_note->label_text);
1318
+ print_const("}{");
1319
+ print(temp_note->label_text);
1320
+ print_const("}{");
1321
+ print(temp_note->clean_text);
1322
+ print_const("}");
1323
+
1324
+ printf("\\gls{%s}", temp_note->label_text);
1325
+ }
1326
+ } else {
1327
+ // Note-based syntax disabled
1328
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1329
+ }
1330
+
1331
+ break;
1332
+
1333
+ case PAIR_BRACKET_CITATION:
1334
+ parse_citation:
1335
+ temp_bool = true; // Track whether this is regular vs 'not cited'
1336
+ temp_token = t; // Remember whether we need to skip ahead
1337
+
1338
+ if (scratch->extensions & EXT_NOTES) {
1339
+ // Note-based syntax enabled
1340
+
1341
+ if (t->type == PAIR_BRACKET) {
1342
+ // This is a locator for a subsequent citation (e.g. `[foo][#bar]`)
1343
+ temp_char = text_inside_pair(source, t);
1344
+ temp_char2 = label_from_string(temp_char);
1345
+
1346
+ if (strcmp(temp_char2, "notcited") == 0) {
1347
+ free(temp_char);
1348
+ temp_char = my_strdup("");
1349
+ temp_bool = false;
1350
+ }
1351
+
1352
+ free(temp_char2);
1353
+
1354
+ // Move ahead to actual citation
1355
+ t = t->next;
1356
+ } else {
1357
+ // This is the actual citation (e.g. `[#foo]`)
1358
+ // No locator
1359
+ temp_char = my_strdup("");
1360
+ }
1361
+
1362
+ // Classify this use
1363
+ citation_from_bracket(source, scratch, t, &temp_short);
1364
+
1365
+ if (temp_short == -1) {
1366
+ // Ensure we aren't using BibTeX
1367
+ if (!scratch->bibtex_file) {
1368
+ // This instance is not properly formed
1369
+ print_const("[#");
1370
+ mmd_export_token_tree_latex(out, source, t->child->next, scratch);
1371
+ print_const("]");
1372
+
1373
+ free(temp_char);
1374
+ break;
1375
+ }
1376
+ }
1377
+
1378
+ // Get instance of the note used
1379
+ if (temp_short == -1) {
1380
+ temp_note = NULL;
1381
+ } else {
1382
+ temp_note = stack_peek_index(scratch->used_citations, temp_short - 1);
1383
+ }
1384
+
1385
+ if (temp_bool) {
1386
+ // This is a regular citation
1387
+
1388
+ // Are we citep vs citet?
1389
+ temp_char2 = clean_inside_pair(source, t, false);
1390
+
1391
+ if (temp_char2[strlen(temp_char2) - 1] == ';') {
1392
+ temp_bool = true; // citet
1393
+ temp_char2[strlen(temp_char2) - 1] = '\0';
1394
+ } else {
1395
+ temp_bool = false; // citep
1396
+ }
1397
+
1398
+ if (temp_char[0] == '\0') {
1399
+ // No locator
1400
+ if (temp_bool) {
1401
+ print_const("\\citet");
1402
+ } else {
1403
+ print_const("~\\citep");
1404
+ }
1405
+ } else {
1406
+ // Locator present
1407
+
1408
+ // Are there two arguments in the locator?
1409
+ // e.g. `[foo\]\[bar]`
1410
+ temp_char3 = strstr(temp_char, "\\]\\[");
1411
+
1412
+ if (temp_char3) {
1413
+ // Convert `\]\[` to `][`
1414
+ temp_char[temp_char3 - temp_char] = ']';
1415
+ memmove(temp_char3 + 1, temp_char3 + 3, strlen(temp_char3) - 2);
1416
+ }
1417
+
1418
+ if (temp_bool) {
1419
+ printf("\\citet[%s]", temp_char);
1420
+ } else {
1421
+ printf("~\\citep[%s]", temp_char);
1422
+ }
1423
+ }
1424
+
1425
+ if (temp_note) {
1426
+ printf("{%s}", temp_note->label_text);
1427
+ } else {
1428
+ printf("{%s}", &temp_char2[1]);
1429
+ }
1430
+
1431
+ free(temp_char2);
1432
+ } else {
1433
+ // This is a "nocite"
1434
+ if (temp_note) {
1435
+ printf("~\\nocite{%s}", temp_note->label_text);
1436
+ } else {
1437
+ temp_char2 = clean_inside_pair(source, t, false);
1438
+ printf("~\\nocite{%s}", &temp_char2[1]);
1439
+ free(temp_char2);
1440
+ }
1441
+ }
1442
+
1443
+ if (temp_token != t) {
1444
+ // Skip citation on next pass
1445
+ scratch->skip_token = 1;
1446
+ }
1447
+
1448
+ free(temp_char);
1449
+ } else {
1450
+ // Note-based syntax disabled
1451
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1452
+ }
1453
+
1454
+ break;
1455
+
1456
+ case PAIR_BRACKET_FOOTNOTE:
1457
+ if (scratch->extensions & EXT_NOTES) {
1458
+ // Note-based syntax enabled
1459
+
1460
+ // Classify this use
1461
+ temp_short2 = scratch->used_footnotes->size;
1462
+ footnote_from_bracket(source, scratch, t, &temp_short);
1463
+
1464
+ if (temp_short == -1) {
1465
+ // This instance is not properly formed
1466
+ print_const("[?");
1467
+ mmd_export_token_tree_latex(out, source, t->child->next, scratch);
1468
+ print_const("]");
1469
+ break;
1470
+ }
1471
+
1472
+ if (temp_short2 == scratch->used_footnotes->size) {
1473
+ // This is a re-use of a previously used note
1474
+
1475
+ // TODO: This would work, assuming no URL's are converted to
1476
+ // footnotes without affecting the numbering.
1477
+ // Could add a NULL to the used_footnotes stack??
1478
+
1479
+ // Additionally, re-using an old footnote would require flipping back
1480
+ // through the document to find it...
1481
+
1482
+ // printf("\\footnotemark[%d]", temp_short);
1483
+
1484
+ print_const("\\footnote{");
1485
+ temp_note = stack_peek_index(scratch->used_footnotes, temp_short - 1);
1486
+
1487
+ mmd_export_token_tree_latex(out, source, temp_note->content, scratch);
1488
+ print_const("}");
1489
+ } else {
1490
+ // This is the first time this note was used
1491
+
1492
+ print_const("\\footnote{");
1493
+ temp_note = stack_peek_index(scratch->used_footnotes, temp_short - 1);
1494
+
1495
+ mmd_export_token_tree_latex(out, source, temp_note->content, scratch);
1496
+ print_const("}");
1497
+ }
1498
+ } else {
1499
+ // Note-based syntax disabled
1500
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1501
+ }
1502
+
1503
+ break;
1504
+
1505
+ case PAIR_BRACKET_GLOSSARY:
1506
+
1507
+ // Which might also be an "auto-tagged" glossary
1508
+ if (scratch->extensions & EXT_NOTES) {
1509
+ // Note-based syntax enabled
1510
+
1511
+ // Classify this use
1512
+ temp_short2 = scratch->used_glossaries->size;
1513
+ temp_short3 = scratch->inline_glossaries_to_free->size;
1514
+ glossary_from_bracket(source, scratch, t, &temp_short);
1515
+
1516
+ if (temp_short == -1) {
1517
+ // This instance is not properly formed
1518
+ print_const("[?");
1519
+
1520
+ if (t->child) {
1521
+ mmd_export_token_tree_latex(out, source, t->child->next, scratch);
1522
+ } else {
1523
+ print_token(t);
1524
+ }
1525
+
1526
+ print_const("]");
1527
+ break;
1528
+ }
1529
+
1530
+ // Get instance of the note used
1531
+ temp_note = stack_peek_index(scratch->used_glossaries, temp_short - 1);
1532
+
1533
+ if (temp_short2 == scratch->used_glossaries->size) {
1534
+ // This is a re-use of a previously used note
1535
+
1536
+ print("\\gls{");
1537
+ print(temp_note->clean_text);
1538
+ print("}");
1539
+ } else {
1540
+ // This is the first time this note was used
1541
+
1542
+ if (temp_short3 == scratch->inline_glossaries_to_free->size) {
1543
+ // This is a reference definition
1544
+ print_const("\\gls{");
1545
+ print(temp_note->clean_text);
1546
+ print_const("}");
1547
+ } else {
1548
+ // This is an inline definition
1549
+ print_const("\\newglossaryentry{");
1550
+ print(temp_note->clean_text);
1551
+
1552
+ print_const("}{name=");
1553
+ print(temp_note->clean_text);
1554
+
1555
+ print_const(", description={");
1556
+
1557
+ // We skip over temp_note->content, since that is the term in use
1558
+ mmd_export_token_tree_latex(out, source, temp_note->content, scratch);
1559
+ print_const("}}\\gls{");
1560
+ print(temp_note->clean_text);
1561
+ print_const("}");
1562
+ }
1563
+ }
1564
+ } else {
1565
+ // Note-based syntax disabled
1566
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1567
+ }
1568
+
1569
+ break;
1570
+
1571
+ case PAIR_BRACKET_VARIABLE:
1572
+ temp_char = text_inside_pair(source, t);
1573
+ temp_char2 = extract_metadata(scratch, temp_char);
1574
+
1575
+ if (temp_char2) {
1576
+ mmd_print_string_latex(out, temp_char2);
1577
+ } else {
1578
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1579
+ }
1580
+
1581
+ // Don't free temp_char2 (it belongs to meta *)
1582
+ free(temp_char);
1583
+ break;
1584
+
1585
+ case PAIR_CRITIC_ADD:
1586
+
1587
+ // Ignore if we're rejecting
1588
+ if (scratch->extensions & EXT_CRITIC_REJECT) {
1589
+ break;
1590
+ }
1591
+
1592
+ if (scratch->extensions & EXT_CRITIC) {
1593
+ t->child->type = TEXT_EMPTY;
1594
+ t->child->mate->type = TEXT_EMPTY;
1595
+
1596
+ if (scratch->extensions & EXT_CRITIC_ACCEPT) {
1597
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1598
+ } else {
1599
+ print_const("\\underline{");
1600
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1601
+ print_const("}");
1602
+ }
1603
+ } else {
1604
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1605
+ }
1606
+
1607
+ break;
1608
+
1609
+ case PAIR_CRITIC_DEL:
1610
+
1611
+ // Ignore if we're accepting
1612
+ if (scratch->extensions & EXT_CRITIC_ACCEPT) {
1613
+ break;
1614
+ }
1615
+
1616
+ if (scratch->extensions & EXT_CRITIC) {
1617
+ t->child->type = TEXT_EMPTY;
1618
+ t->child->mate->type = TEXT_EMPTY;
1619
+
1620
+ if (scratch->extensions & EXT_CRITIC_REJECT) {
1621
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1622
+ } else {
1623
+ print_const("\\sout{");
1624
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1625
+ print_const("}");
1626
+ }
1627
+ } else {
1628
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1629
+ }
1630
+
1631
+ break;
1632
+
1633
+ case PAIR_CRITIC_COM:
1634
+
1635
+ // Ignore if we're rejecting or accepting
1636
+ if ((scratch->extensions & EXT_CRITIC_REJECT) ||
1637
+ (scratch->extensions & EXT_CRITIC_ACCEPT)) {
1638
+ break;
1639
+ }
1640
+
1641
+ if (scratch->extensions & EXT_CRITIC) {
1642
+ t->child->type = TEXT_EMPTY;
1643
+ t->child->mate->type = TEXT_EMPTY;
1644
+ print_const("\\cmnote{");
1645
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1646
+ print_const("}");
1647
+ } else {
1648
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1649
+ }
1650
+
1651
+ break;
1652
+
1653
+ case PAIR_CRITIC_HI:
1654
+
1655
+ // Ignore if we're rejecting or accepting
1656
+ if ((scratch->extensions & EXT_CRITIC_REJECT) ||
1657
+ (scratch->extensions & EXT_CRITIC_ACCEPT)) {
1658
+ t->child->type = TEXT_EMPTY;
1659
+ t->child->mate->type = TEXT_EMPTY;
1660
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1661
+ break;
1662
+ }
1663
+
1664
+ if (scratch->extensions & EXT_CRITIC) {
1665
+ t->child->type = TEXT_EMPTY;
1666
+ t->child->mate->type = TEXT_EMPTY;
1667
+ // 'hl' requires 'soul' package
1668
+ print_const("\\hl{");
1669
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1670
+ print_const("}");
1671
+ } else {
1672
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1673
+ }
1674
+
1675
+ break;
1676
+
1677
+ case CRITIC_SUB_DIV_A:
1678
+ print_const("~");
1679
+ break;
1680
+
1681
+ case CRITIC_SUB_DIV_B:
1682
+ print_const("&gt;");
1683
+ break;
1684
+
1685
+ case PAIR_CRITIC_SUB_DEL:
1686
+ if ((scratch->extensions & EXT_CRITIC) &&
1687
+ (t->next) &&
1688
+ (t->next->type == PAIR_CRITIC_SUB_ADD)) {
1689
+ t->child->type = TEXT_EMPTY;
1690
+ t->child->mate->type = TEXT_EMPTY;
1691
+
1692
+ if (scratch->extensions & EXT_CRITIC_ACCEPT) {
1693
+
1694
+ } else if (scratch->extensions & EXT_CRITIC_REJECT) {
1695
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1696
+ } else {
1697
+ print_const("\\sout{");
1698
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1699
+ print_const("}");
1700
+ }
1701
+ } else {
1702
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1703
+ }
1704
+
1705
+ break;
1706
+
1707
+ case PAIR_CRITIC_SUB_ADD:
1708
+ if ((scratch->extensions & EXT_CRITIC) &&
1709
+ (t->prev) &&
1710
+ (t->prev->type == PAIR_CRITIC_SUB_DEL)) {
1711
+ t->child->type = TEXT_EMPTY;
1712
+ t->child->mate->type = TEXT_EMPTY;
1713
+
1714
+ if (scratch->extensions & EXT_CRITIC_REJECT) {
1715
+
1716
+ } else if (scratch->extensions & EXT_CRITIC_ACCEPT) {
1717
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1718
+ } else {
1719
+ print_const("\\underline{");
1720
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1721
+ print_const("}");
1722
+ }
1723
+ } else {
1724
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1725
+ }
1726
+
1727
+ break;
1728
+
1729
+ case PAIR_HTML_COMMENT:
1730
+ break;
1731
+
1732
+ case PAIR_MATH:
1733
+ if (strncmp(&source[t->child->start + t->child->len], "\\begin", 6) != 0) {
1734
+ mmd_export_token_latex(out, source, t->child, scratch);
1735
+ }
1736
+
1737
+ // Math is raw LaTeX -- use string itself rather than interior tokens
1738
+ d_string_append_c_array(out, &(source[t->child->start + t->child->len]), t->child->mate->start - t->child->start - t->child->len);
1739
+
1740
+ if (strncmp(&source[t->child->start + t->child->len], "\\begin", 6) != 0) {
1741
+ mmd_export_token_latex(out, source, t->child->mate, scratch);
1742
+ }
1743
+
1744
+ break;
1745
+
1746
+ case PAIR_EMPH:
1747
+ case PAIR_PAREN:
1748
+ case PAIR_QUOTE_DOUBLE:
1749
+ case PAIR_QUOTE_SINGLE:
1750
+ case PAIR_STAR:
1751
+ case PAIR_STRONG:
1752
+ case PAIR_SUBSCRIPT:
1753
+ case PAIR_SUPERSCRIPT:
1754
+ case PAIR_UL:
1755
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1756
+ break;
1757
+
1758
+ case PAREN_LEFT:
1759
+ print_const("(");
1760
+ break;
1761
+
1762
+ case PAREN_RIGHT:
1763
+ print_const(")");
1764
+ break;
1765
+
1766
+ case PIPE:
1767
+ for (int i = 0; i < t->len; ++i) {
1768
+ print_const("\\textbar{}");
1769
+ }
1770
+
1771
+ break;
1772
+
1773
+ case PLUS:
1774
+ print_token(t);
1775
+ break;
1776
+
1777
+ case QUOTE_SINGLE:
1778
+ if ((t->mate == NULL) || (!(scratch->extensions & EXT_SMART))) {
1779
+ print_const("'");
1780
+ } else {
1781
+ (t->start < t->mate->start) ? ( print_localized(QUOTE_LEFT_SINGLE) ) : ( print_localized(QUOTE_RIGHT_SINGLE) );
1782
+ }
1783
+
1784
+ break;
1785
+
1786
+ case QUOTE_DOUBLE:
1787
+ if ((t->mate == NULL) || (!(scratch->extensions & EXT_SMART))) {
1788
+ print_const("''");
1789
+ } else {
1790
+ (t->start < t->mate->start) ? ( print_localized(QUOTE_LEFT_DOUBLE) ) : ( print_localized(QUOTE_RIGHT_DOUBLE) );
1791
+ }
1792
+
1793
+ break;
1794
+
1795
+ case QUOTE_RIGHT_ALT:
1796
+ if ((t->mate == NULL) || (!(scratch->extensions & EXT_SMART))) {
1797
+ print_const("''");
1798
+ } else {
1799
+ print_localized(QUOTE_RIGHT_DOUBLE);
1800
+ }
1801
+
1802
+ break;
1803
+
1804
+ case SLASH:
1805
+ print_const("\\slash ");
1806
+ break;
1807
+
1808
+ case STAR:
1809
+ print_token(t);
1810
+ break;
1811
+
1812
+ case STRONG_START:
1813
+ print_const("\\textbf{");
1814
+ break;
1815
+
1816
+ case STRONG_STOP:
1817
+ print_const("}");
1818
+ break;
1819
+
1820
+ case SUBSCRIPT:
1821
+ if (t->mate) {
1822
+ (t->start < t->mate->start) ? (print_const("\\textsubscript{")) : (print_const("}"));
1823
+ } else if (t->len != 1) {
1824
+ print_const("\\textsubscript{");
1825
+ mmd_export_token_latex(out, source, t->child, scratch);
1826
+ print_const("}");
1827
+ } else {
1828
+ print_const("\\ensuremath{\\sim}");
1829
+ }
1830
+
1831
+ break;
1832
+
1833
+ case SUPERSCRIPT:
1834
+ if (t->mate) {
1835
+ (t->start < t->mate->start) ? (print_const("\\textsuperscript{")) : (print_const("}"));
1836
+ } else if (t->len != 1) {
1837
+ print_const("\\textsuperscript{");
1838
+ mmd_export_token_latex(out, source, t->child, scratch);
1839
+ print_const("}");
1840
+ } else {
1841
+ print_const("\\^{}");
1842
+ }
1843
+
1844
+ break;
1845
+
1846
+ case TABLE_CELL:
1847
+ if (t->next && t->next->type == TABLE_DIVIDER) {
1848
+ if (t->next->len > 1) {
1849
+ printf("\\multicolumn{%lu}{", t->next->len);
1850
+
1851
+ switch (scratch->table_alignment[scratch->table_cell_count]) {
1852
+ case 'l':
1853
+ case 'L':
1854
+ print_const("l}{");
1855
+ break;
1856
+
1857
+ case 'r':
1858
+ case 'R':
1859
+ print_const("r}{");
1860
+ break;
1861
+
1862
+ default:
1863
+ print_const("c}{");
1864
+ break;
1865
+ }
1866
+ }
1867
+ }
1868
+
1869
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1870
+
1871
+ if (t->next && t->next->type == TABLE_DIVIDER) {
1872
+ if (t->next->len > 1) {
1873
+ print_const("}");
1874
+ }
1875
+ }
1876
+
1877
+ if (t->next && t->next->type == TABLE_DIVIDER) {
1878
+ t = t->next;
1879
+
1880
+ if (t->next && t->next->type == TABLE_CELL) {
1881
+ print_const("&");
1882
+ scratch->table_cell_count += t->next->len;
1883
+ }
1884
+ } else {
1885
+ scratch->table_cell_count++;
1886
+ }
1887
+
1888
+ break;
1889
+
1890
+ case TABLE_DIVIDER:
1891
+ break;
1892
+
1893
+ case TABLE_ROW:
1894
+ scratch->table_cell_count = 0;
1895
+ mmd_export_token_tree_latex(out, source, t->child, scratch);
1896
+ print_const("\\\\\n");
1897
+ break;
1898
+
1899
+ case TEXT_BACKSLASH:
1900
+ print_const("\\textbackslash{}");
1901
+ break;
1902
+
1903
+ case TEXT_EMPTY:
1904
+ break;
1905
+
1906
+ case TEXT_HASH:
1907
+ print_const("\\#");
1908
+ break;
1909
+
1910
+ case TEXT_LINEBREAK:
1911
+ if (t->next) {
1912
+ print_const("\\\\\n");
1913
+ scratch->padded = 1;
1914
+ }
1915
+
1916
+ break;
1917
+
1918
+ case TEXT_NL:
1919
+ if (t->next) {
1920
+ print_char('\n');
1921
+ }
1922
+
1923
+ break;
1924
+
1925
+ case TEXT_PERCENT:
1926
+ print_const("\\%");
1927
+ break;
1928
+
1929
+ case TEXT_BRACE_LEFT:
1930
+ case TEXT_BRACE_RIGHT:
1931
+ print_const("\\");
1932
+
1933
+ case RAW_FILTER_LEFT:
1934
+ case TEXT_NUMBER_POSS_LIST:
1935
+ case TEXT_PERIOD:
1936
+ case TEXT_PLAIN:
1937
+ print_token(t);
1938
+ break;
1939
+
1940
+ case TOC:
1941
+ print_const("\\{\\{TOC\\}\\}");
1942
+ break;
1943
+
1944
+ case UL:
1945
+ print_const("\\_");
1946
+ break;
1947
+
1948
+ default:
1949
+ fprintf(stderr, "Unknown token type: %d\n", t->type);
1950
+ token_describe(t, source);
1951
+ break;
1952
+ }
1953
+ }
1954
+
1955
+
1956
+ void mmd_export_token_tree_latex(DString * out, const char * source, token * t, scratch_pad * scratch) {
1957
+
1958
+ // Prevent stack overflow with "dangerous" input causing extreme recursion
1959
+ if (scratch->recurse_depth == kMaxExportRecursiveDepth) {
1960
+ return;
1961
+ }
1962
+
1963
+ scratch->recurse_depth++;
1964
+
1965
+ while (t != NULL) {
1966
+ if (scratch->skip_token) {
1967
+ scratch->skip_token--;
1968
+ } else {
1969
+ mmd_export_token_latex(out, source, t, scratch);
1970
+ }
1971
+
1972
+ t = t->next;
1973
+ }
1974
+
1975
+ scratch->recurse_depth--;
1976
+ }
1977
+
1978
+
1979
+ void mmd_export_token_latex_raw(DString * out, const char * source, token * t, scratch_pad * scratch) {
1980
+ if (t == NULL) {
1981
+ return;
1982
+ }
1983
+
1984
+ switch (t->type) {
1985
+ case ESCAPED_CHARACTER:
1986
+ print_const("\\");
1987
+ print_char(source[t->start + 1]);
1988
+ // mmd_print_char_latex(out, source[t->start + 1]);
1989
+ break;
1990
+
1991
+ case HTML_ENTITY:
1992
+ print_token(t);
1993
+ break;
1994
+
1995
+ case SUBSCRIPT:
1996
+ if (t->child) {
1997
+ print_const("\\ensuremath{\\sim}");
1998
+ mmd_export_token_tree_latex_raw(out, source, t->child, scratch);
1999
+ } else {
2000
+ print_token(t);
2001
+ }
2002
+
2003
+ break;
2004
+
2005
+ case SUPERSCRIPT:
2006
+ if (t->child) {
2007
+ print_const("\\^{}");
2008
+ mmd_export_token_tree_latex_raw(out, source, t->child, scratch);
2009
+ } else {
2010
+ print_token(t);
2011
+ }
2012
+
2013
+ break;
2014
+
2015
+ case CODE_FENCE:
2016
+ if (t->next) {
2017
+ t->next->type = TEXT_EMPTY;
2018
+ }
2019
+
2020
+ case TEXT_EMPTY:
2021
+ break;
2022
+
2023
+ default:
2024
+ if (t->child) {
2025
+ mmd_export_token_tree_latex_raw(out, source, t->child, scratch);
2026
+ } else {
2027
+ print_token(t);
2028
+ }
2029
+
2030
+ break;
2031
+ }
2032
+ }
2033
+
2034
+
2035
+ void mmd_export_token_tree_latex_raw(DString * out, const char * source, token * t, scratch_pad * scratch) {
2036
+ while (t != NULL) {
2037
+ if (scratch->skip_token) {
2038
+ scratch->skip_token--;
2039
+ } else {
2040
+ mmd_export_token_latex_raw(out, source, t, scratch);
2041
+ }
2042
+
2043
+ t = t->next;
2044
+ }
2045
+ }
2046
+
2047
+
2048
+ void mmd_export_token_latex_tt(DString * out, const char * source, token * t, scratch_pad * scratch) {
2049
+ if (t == NULL) {
2050
+ return;
2051
+ }
2052
+
2053
+ switch (t->type) {
2054
+ case AMPERSAND:
2055
+ case AMPERSAND_LONG:
2056
+ print_const("\\&");
2057
+ break;
2058
+
2059
+ case ANGLE_LEFT:
2060
+ print_const("<");
2061
+ break;
2062
+
2063
+ case ANGLE_RIGHT:
2064
+ print_const(">");
2065
+ break;
2066
+
2067
+ case CRITIC_ADD_OPEN:
2068
+ print_const("\\{++");
2069
+ break;
2070
+
2071
+ case CRITIC_ADD_CLOSE:
2072
+ print_const("++\\}");
2073
+ break;
2074
+
2075
+ case CRITIC_COM_OPEN:
2076
+ print_const("\\{>>");
2077
+ break;
2078
+
2079
+ case CRITIC_COM_CLOSE:
2080
+ print_const("<<\\}");
2081
+ break;
2082
+
2083
+ case CRITIC_DEL_OPEN:
2084
+ print_const("\\{--");
2085
+ break;
2086
+
2087
+ case CRITIC_DEL_CLOSE:
2088
+ print_const("--\\}");
2089
+ break;
2090
+
2091
+ case CRITIC_HI_OPEN:
2092
+ print_const("\\{==");
2093
+ break;
2094
+
2095
+ case CRITIC_HI_CLOSE:
2096
+ print_const("==\\}");
2097
+ break;
2098
+
2099
+ case CRITIC_SUB_OPEN:
2100
+ print_const("\\{~~");
2101
+ break;
2102
+
2103
+ case CRITIC_SUB_DIV:
2104
+ print_const("~>");
2105
+ break;
2106
+
2107
+ case CRITIC_SUB_CLOSE:
2108
+ print_const("~~\\}");
2109
+ break;
2110
+
2111
+ case DASH_N:
2112
+ if (t->len == 1) {
2113
+ print_const("-");
2114
+ } else {
2115
+ print_const("-{}-");
2116
+ }
2117
+
2118
+ break;
2119
+
2120
+ case DASH_M:
2121
+ print_const("-{}-{}-");
2122
+ break;
2123
+
2124
+ case EMPH_START:
2125
+ case EMPH_STOP:
2126
+ if (source[t->start] == '_') {
2127
+ print_const("\\_");
2128
+ } else {
2129
+ print_const("*");
2130
+ }
2131
+
2132
+ break;
2133
+
2134
+ case ESCAPED_CHARACTER:
2135
+ print_const("\\textbackslash{}");
2136
+ mmd_print_char_latex(out, source[t->start + 1]);
2137
+ break;
2138
+
2139
+ case HTML_ENTITY:
2140
+ if (source[t->start + 1] == '#') {
2141
+ print_const("\\&\\#");
2142
+ d_string_append_c_array(out, &(source[t->start + 2]), t->len - 2);
2143
+ } else {
2144
+ print_const("\\");
2145
+ print_token(t);
2146
+ }
2147
+
2148
+ break;
2149
+
2150
+ case CODE_FENCE:
2151
+ if (t->next) {
2152
+ t->next->type = TEXT_EMPTY;
2153
+ }
2154
+
2155
+ case MATH_BRACKET_OPEN:
2156
+ case MATH_BRACKET_CLOSE:
2157
+ case MATH_PAREN_OPEN:
2158
+ case MATH_PAREN_CLOSE:
2159
+ print_const("\\textbackslash{}\\textbackslash{}");
2160
+ print_char(source[t->start + 2]);
2161
+ break;
2162
+
2163
+ case TEXT_EMPTY:
2164
+ break;
2165
+
2166
+ case SLASH:
2167
+ print_const("\\slash ");
2168
+ break;
2169
+
2170
+ case TEXT_BACKSLASH:
2171
+ print_const("\\textbackslash{}");
2172
+ break;
2173
+
2174
+ case BRACE_DOUBLE_LEFT:
2175
+ print_const("\\{\\{");
2176
+ break;
2177
+
2178
+ case BRACE_DOUBLE_RIGHT:
2179
+ print_const("\\}\\}");
2180
+ break;
2181
+
2182
+ case SUBSCRIPT:
2183
+ if (t->child) {
2184
+ print_const("\\ensuremath{\\sim}");
2185
+ mmd_export_token_tree_latex_tt(out, source, t->child, scratch);
2186
+ } else {
2187
+ print_const("\\ensuremath{\\sim}");
2188
+ }
2189
+
2190
+ break;
2191
+
2192
+ case SUPERSCRIPT:
2193
+ if (t->child) {
2194
+ print_const("\\^{}");
2195
+ mmd_export_token_tree_latex_tt(out, source, t->child, scratch);
2196
+ } else {
2197
+ print_const("\\^{}");
2198
+ }
2199
+
2200
+ break;
2201
+
2202
+ case TEXT_BRACE_LEFT:
2203
+ print_const("\\{");
2204
+ break;
2205
+
2206
+ case TEXT_BRACE_RIGHT:
2207
+ print_const("\\}");
2208
+ break;
2209
+
2210
+ case TOC:
2211
+ print_const("\\{\\{TOC\\}\\}");
2212
+ break;
2213
+
2214
+ case UL:
2215
+ print_const("\\_");
2216
+ break;
2217
+
2218
+ default:
2219
+ if (t->child) {
2220
+ mmd_export_token_tree_latex_tt(out, source, t->child, scratch);
2221
+ } else {
2222
+ print_token(t);
2223
+ }
2224
+
2225
+ break;
2226
+ }
2227
+ }
2228
+
2229
+
2230
+ void mmd_export_token_tree_latex_tt(DString * out, const char * source, token * t, scratch_pad * scratch) {
2231
+ while (t != NULL) {
2232
+ if (scratch->skip_token) {
2233
+ scratch->skip_token--;
2234
+ } else {
2235
+ mmd_export_token_latex_tt(out, source, t, scratch);
2236
+ }
2237
+
2238
+ t = t->next;
2239
+ }
2240
+ }
2241
+
2242
+ int clean_text_sort(fn_holder * a, fn_holder * b) {
2243
+ return strcmp(a->note->clean_text, b->note->clean_text);
2244
+ }
2245
+
2246
+
2247
+
2248
+ void mmd_define_glossaries_latex(DString * out, const char * source, scratch_pad * scratch) {
2249
+ // Iterate through glossary definitions
2250
+ fn_holder * f, * f_tmp;
2251
+
2252
+ // Sort glossary entries
2253
+ HASH_SORT(scratch->glossary_hash, clean_text_sort);
2254
+
2255
+ char * last_key = NULL;
2256
+
2257
+ HASH_ITER(hh, scratch->glossary_hash, f, f_tmp) {
2258
+ if (!last_key || strcmp(last_key, f->note->clean_text) != 0) {
2259
+ // Add this glossary definition
2260
+ print_const("\\longnewglossaryentry{");
2261
+ print(f->note->clean_text);
2262
+
2263
+ print_const("}{name=");
2264
+ print(f->note->clean_text);
2265
+ print_const("}{");
2266
+
2267
+ mmd_export_token_tree_latex(out, source, f->note->content, scratch);
2268
+ print_const("}\n\n");
2269
+ }
2270
+
2271
+ last_key = f->note->clean_text;
2272
+ }
2273
+
2274
+ // And abbreviations
2275
+
2276
+ HASH_ITER(hh, scratch->abbreviation_hash, f, f_tmp) {
2277
+ // Add this abbreviation definition
2278
+ print_const("\\newacronym{");
2279
+ print(f->note->label_text);
2280
+ print_const("}{");
2281
+ print(f->note->label_text);
2282
+ print_const("}{");
2283
+ print(f->note->clean_text);
2284
+ print_const("}\n\n");
2285
+ }
2286
+ }
2287
+
2288
+
2289
+ void mmd_start_complete_latex(DString * out, const char * source, scratch_pad * scratch) {
2290
+ // Iterate over metadata keys
2291
+ meta * m;
2292
+
2293
+ m = extract_meta_from_stack(scratch, "latexleader");
2294
+
2295
+ if (m) {
2296
+ printf("\\input{%s}\n", m->value);
2297
+ } else {
2298
+ m = extract_meta_from_stack(scratch, "latexconfig");
2299
+
2300
+ if (m) {
2301
+ printf("\\input{mmd6-%s-leader}\n", m->value);
2302
+ }
2303
+ }
2304
+
2305
+ for (m = scratch->meta_hash; m != NULL; m = m->hh.next) {
2306
+ if (strcmp(m->key, "baseheaderlevel") == 0) {
2307
+ } else if (strcmp(m->key, "css") == 0) {
2308
+ } else if (strcmp(m->key, "htmlfooter") == 0) {
2309
+ } else if (strcmp(m->key, "htmlheader") == 0) {
2310
+ } else if (strcmp(m->key, "htmlheaderlevel") == 0) {
2311
+ } else if (strcmp(m->key, "language") == 0) {
2312
+ } else if (strcmp(m->key, "latexbegin") == 0) {
2313
+ } else if (strcmp(m->key, "latexconfig") == 0) {
2314
+ } else if (strcmp(m->key, "latexheader") == 0) {
2315
+ print(m->value);
2316
+ print_char('\n');
2317
+ } else if (strcmp(m->key, "latexfooter") == 0) {
2318
+ } else if (strcmp(m->key, "latexheaderlevel") == 0) {
2319
+ } else if (strcmp(m->key, "latexinput") == 0) {
2320
+ } else if (strcmp(m->key, "latexleader") == 0) {
2321
+ } else if (strcmp(m->key, "latexmode") == 0) {
2322
+ } else if (strcmp(m->key, "mmdfooter") == 0) {
2323
+ } else if (strcmp(m->key, "mmdheader") == 0) {
2324
+ } else if (strcmp(m->key, "quoteslanguage") == 0) {
2325
+ } else if (strcmp(m->key, "title") == 0) {
2326
+ print_const("\\def\\mytitle{");
2327
+ mmd_print_string_latex(out, m->value);
2328
+ print_const("}\n");
2329
+ } else if (strcmp(m->key, "latextitle") == 0) {
2330
+ print_const("\\def\\latextitle{");
2331
+ print(m->value);
2332
+ print_const("}\n");
2333
+ } else if (strcmp(m->key, "author") == 0) {
2334
+ print_const("\\def\\myauthor{");
2335
+ mmd_print_string_latex(out, m->value);
2336
+ print_const("}\n");
2337
+ } else if (strcmp(m->key, "latexauthor") == 0) {
2338
+ print_const("\\def\\latexauthor{");
2339
+ print(m->value);
2340
+ print_const("}\n");
2341
+ } else if (strcmp(m->key, "date") == 0) {
2342
+ print_const("\\def\\mydate{");
2343
+ mmd_print_string_latex(out, m->value);
2344
+ print_const("}\n");
2345
+ } else if (strcmp(m->key, "copyright") == 0) {
2346
+ print_const("\\def\\mycopyright{");
2347
+ mmd_print_string_latex(out, m->value);
2348
+ print_const("}\n");
2349
+ } else if (strcmp(m->key, "bibtex") == 0) {
2350
+ print_const("\\def\\bibliocommand{\\bibliography{");
2351
+ mmd_print_string_latex(out, m->value);
2352
+ print_const("}}\n");
2353
+ } else if (strcmp(m->key, "transcludebase") == 0) {
2354
+ } else if (strcmp(m->key, "xhtmlheader") == 0) {
2355
+ } else if (strcmp(m->key, "xhtmlheaderlevel") == 0) {
2356
+ } else {
2357
+ print_const("\\def\\");
2358
+ mmd_print_string_latex(out, m->key);
2359
+ print_const("{");
2360
+ mmd_print_string_latex(out, m->value);
2361
+ print_const("}\n");
2362
+ }
2363
+ }
2364
+
2365
+ // Define glossaries in preamble for more flexibility
2366
+ mmd_define_glossaries_latex(out, source, scratch);
2367
+
2368
+ m = extract_meta_from_stack(scratch, "latexbegin");
2369
+
2370
+ if (m) {
2371
+ printf("\\input{%s}\n", m->value);
2372
+ } else {
2373
+ m = extract_meta_from_stack(scratch, "latexconfig");
2374
+
2375
+ if (m) {
2376
+ printf("\\input{mmd6-%s-begin}\n", m->value);
2377
+ }
2378
+ }
2379
+
2380
+ scratch->padded = 1;
2381
+ }
2382
+
2383
+
2384
+ void mmd_end_complete_latex(DString * out, const char * source, scratch_pad * scratch) {
2385
+ pad(out, 2, scratch);
2386
+
2387
+ meta * m = extract_meta_from_stack(scratch, "latexfooter");
2388
+
2389
+ if (m) {
2390
+ printf("\\input{%s}\n\n", m->value);
2391
+ } else {
2392
+ m = extract_meta_from_stack(scratch, "latexconfig");
2393
+
2394
+ if (m) {
2395
+ printf("\\input{mmd6-%s-footer}\n", m->value);
2396
+ }
2397
+ }
2398
+
2399
+ print_const("\\end{document}");
2400
+ scratch->padded = 0;
2401
+ }
2402
+
2403
+
2404
+ void mmd_export_citation_list_latex(DString * out, const char * source, scratch_pad * scratch) {
2405
+ if (scratch->used_citations->size > 0) {
2406
+ footnote * note;
2407
+ token * content;
2408
+
2409
+ pad(out, 2, scratch);
2410
+ print_const("\\begin{thebibliography}{0}");
2411
+ scratch->padded = 0;
2412
+
2413
+ for (int i = 0; i < scratch->used_citations->size; ++i) {
2414
+ // Export footnote
2415
+ pad(out, 2, scratch);
2416
+
2417
+ note = stack_peek_index(scratch->used_citations, i);
2418
+ content = note->content;
2419
+
2420
+ printf("\\bibitem{%s}\n", note->label_text);
2421
+ scratch->padded = 6;
2422
+
2423
+ scratch->footnote_para_counter = 0;
2424
+
2425
+ scratch->citation_being_printed = i + 1;
2426
+
2427
+ mmd_export_token_tree_latex(out, source, content, scratch);
2428
+ }
2429
+
2430
+ pad(out, 1, scratch);
2431
+ print_const("\\end{thebibliography}");
2432
+ scratch->padded = 0;
2433
+ scratch->citation_being_printed = 0;
2434
+ }
2435
+ }