rmultimarkdown 4.7.1.1 → 6.2.2.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ }