lib/core/stream: LineIterator use CachedIterator
[nit.git] / lib / markdown2 / markdown_latex_rendering.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # LaTeX rendering of Markdown documents
16 module markdown_latex_rendering
17
18 import markdown_rendering
19 import markdown_github
20 import markdown_wikilinks
21
22 # Markdown document renderer to LaTeX
23 class LatexRenderer
24 super MdRenderer
25
26 # Generate the LaTeX document wrapper
27 #
28 # The header includes:
29 # * document class
30 # * package importation
31 # * begin and end document directives
32 var wrap_document = false is optional, writable
33
34 # LaTeX document class
35 #
36 # Default is `article`.
37 var document_class = "article" is optional, writable
38
39 # LaTeX document page format
40 #
41 # Default is `letter`.
42 var page_format = "letter" is optional, writable
43
44 # LaTeX font size
45 #
46 # Default is `10pt`.
47 var font_size = "10pt" is optional, writable
48
49 # Use `listings` package for code blocks?
50 var use_listings = false is optional, writable
51
52 # LaTeX output under construction
53 private var latex: Buffer is noinit
54
55 # Render `document` as LaTeX
56 redef fun render(document) do
57 latex = new Buffer
58 enter_visit(document)
59 return latex.write_to_string
60 end
61
62 redef fun visit(node) do node.render_latex(self)
63
64 # Indentation level
65 var indent = 0
66
67 # Add a raw `string` to the output
68 #
69 # Raw means that the string will not be escaped.
70 fun add_raw(string: String) do latex.append string
71
72 # Add `text` string to the output
73 #
74 # The string will be escaped.
75 fun add_text(text: String) do latex.append latex_escape(text)
76
77 # Add a blank line to the output
78 fun add_line do
79 if not latex.is_empty and latex.last != '\n' then
80 latex.add '\n'
81 end
82 end
83
84 # Add an indentation depending on `ident` level
85 fun add_indent do latex.append " " * indent
86
87 # Escape `string` to LaTeX
88 fun latex_escape(string: String): String do
89 var buffer = new Buffer
90 for i in [0 .. string.length[ do
91 var c = string.chars[i]
92 if c == '>' then
93 buffer.append "\\textgreater"
94 continue
95 else if c == '<' then
96 buffer.append "\\textless"
97 continue
98 else if c == '\\' then
99 buffer.append "\\textbackslash"
100 continue
101 else if escaped_chars.has(c) then
102 buffer.add '\\'
103 end
104 buffer.add c
105 end
106 return buffer.to_s
107 end
108
109 # LaTeX characters to escape
110 var escaped_chars = ['%', '$', '{', '}', '_', '#', '&']
111 end
112
113 redef class MdNode
114
115 # Render `self` as HTML
116 fun render_latex(v: LatexRenderer) do visit_all(v)
117 end
118
119 # Blocks
120
121 redef class MdDocument
122 redef fun render_latex(v) do
123 var wrap_document = v.wrap_document
124 if v.wrap_document then
125 v.add_line
126 v.add_raw "\\documentclass[{v.page_format},{v.font_size}]\{{v.document_class}\}\n\n"
127 v.add_raw "\\usepackage[utf8]\{inputenc\}\n"
128 if v.use_listings then
129 v.add_raw "\\usepackage\{listings\}\n"
130 end
131 v.add_raw "\\usepackage\{hyperref\}\n"
132 v.add_raw "\\usepackage\{graphicx\}\n"
133 v.add_raw "\\usepackage\{ulem\}\n\n"
134 v.add_raw "\\begin\{document\}\n\n"
135 end
136 var node = first_child
137 while node != null do
138 v.enter_visit node
139 node = node.next
140 if node != null then v.add_raw "\n"
141 end
142 if wrap_document then
143 v.add_raw "\n\\end\{document\}\n"
144 end
145 end
146 end
147
148 redef class MdHeading
149 redef fun render_latex(v) do
150 var level = self.level
151 v.add_indent
152 v.add_line
153 if level == 1 then
154 v.add_raw "\\section\{"
155 else if level == 2 then
156 v.add_raw "\\subsection\{"
157 else if level == 3 then
158 v.add_raw "\\subsubsection\{"
159 else if level == 4 then
160 v.add_raw "\\paragraph\{"
161 else if level == 5 then
162 v.add_raw "\\subparagraph\{"
163 else
164 # use bold for level 6 headings
165 v.add_raw "\\textbf\{"
166 end
167 v.add_indent
168 visit_all(v)
169 v.add_raw "\}"
170 v.add_line
171 end
172 end
173
174 redef class MdBlockQuote
175 redef fun render_latex(v) do
176 v.add_line
177 v.add_indent
178 v.add_raw "\\begin\{quote\}"
179 v.add_line
180 v.indent += 2
181 visit_all(v)
182 v.indent -= 2
183 v.add_line
184 v.add_indent
185 v.add_raw "\\end\{quote\}"
186 v.add_line
187 end
188 end
189
190 redef class MdIndentedCodeBlock
191 redef fun render_latex(v) do
192 var directive = if v.use_listings then "lstlisting" else "verbatim"
193 v.add_line
194 v.add_indent
195 v.add_raw "\\begin\{{directive}\}"
196 v.add_line
197 v.add_raw literal or else ""
198 v.add_line
199 v.add_indent
200 v.add_raw "\\end\{{directive}\}"
201 v.add_line
202 end
203 end
204
205 redef class MdFencedCodeBlock
206 redef fun render_latex(v) do
207 var info = self.info
208 var lstlistings = v.use_listings
209 var directive = if lstlistings then "lstlisting" else "verbatim"
210 v.add_line
211 v.add_indent
212 v.add_raw "\\begin\{{directive}\}"
213 if lstlistings and info != null and not info.is_empty then
214 v.add_raw "[language={info}]"
215 end
216 v.add_line
217 v.add_raw literal or else ""
218 v.add_line
219 v.add_indent
220 v.add_raw "\\end\{{directive}\}"
221 v.add_line
222 end
223 end
224
225 redef class MdOrderedList
226 redef fun render_latex(v) do
227 var start = self.start_number
228 v.add_line
229 v.add_indent
230 v.add_raw "\\begin\{enumerate\}"
231 v.indent += 2
232 v.add_line
233 if start != 1 then
234 v.add_indent
235 v.add_raw "\\setcounter\{enum{nesting_level}\}\{{start}\}"
236 v.add_line
237 end
238 visit_all(v)
239 v.indent -= 2
240 v.add_line
241 v.add_indent
242 v.add_raw "\\end\{enumerate\}"
243 v.add_line
244 end
245
246 # Depth of ordered list
247 #
248 # Used to compute the `setcounter` level.
249 fun nesting_level: String do
250 var nesting = 1
251
252 var parent = self.parent
253 while parent != null do
254 if parent isa MdOrderedList then nesting += 1
255 parent = parent.parent
256 end
257
258 if nesting <= 3 then
259 return "i" * nesting
260 end
261 return "iv"
262 end
263 end
264
265 redef class MdUnorderedList
266 redef fun render_latex(v) do
267 v.add_line
268 v.add_indent
269 v.add_raw "\\begin\{itemize\}"
270 v.add_line
271 v.indent += 2
272 visit_all(v)
273 v.indent -= 2
274 v.add_line
275 v.add_indent
276 v.add_raw "\\end\{itemize\}"
277 v.add_line
278 end
279 end
280
281 redef class MdListItem
282 redef fun render_latex(v) do
283 v.add_indent
284 v.add_raw "\\item"
285 v.add_line
286 v.indent += 2
287 visit_all(v)
288 v.indent -= 2
289 v.add_line
290 end
291 end
292
293 redef class MdThematicBreak
294 redef fun render_latex(v) do
295 v.add_line
296 v.add_indent
297 v.add_raw "\\begin\{center\}\\rule\{3in\}\{0.4pt\}\\end\{center\}"
298 v.add_line
299 end
300 end
301
302 redef class MdParagraph
303 redef fun render_latex(v) do
304 v.add_indent
305 visit_all(v)
306 v.add_line
307 end
308 end
309
310
311 redef class MdHtmlBlock
312 redef fun render_latex(v) do
313 v.add_line
314 v.add_indent
315 v.add_raw "\\begin\{verbatim\}"
316 v.add_line
317 v.add_indent
318 v.add_raw literal or else ""
319 v.add_line
320 v.add_indent
321 v.add_raw "\\end\{verbatim\}"
322 v.add_line
323 end
324 end
325
326 # Inlines
327
328 redef class MdLineBreak
329 redef fun render_latex(v) do
330 v.add_line
331 v.add_indent
332 end
333 end
334
335 redef class MdCode
336 redef fun render_latex(v) do
337 v.add_raw "\\texttt\{"
338 v.add_text literal
339 v.add_raw "\}"
340 end
341 end
342
343 redef class MdEmphasis
344 redef fun render_latex(v) do
345 v.add_raw "\\textit\{"
346 visit_all(v)
347 v.add_raw "\}"
348 end
349 end
350
351 redef class MdStrongEmphasis
352 redef fun render_latex(v) do
353 v.add_raw "\\textbf\{"
354 visit_all(v)
355 v.add_raw "\}"
356 end
357 end
358
359 redef class MdHtmlInline
360 redef fun render_latex(v) do
361 v.add_raw "\\texttt\{"
362 v.add_raw v.latex_escape(literal)
363 v.add_raw "\}"
364 end
365 end
366
367 redef class MdImage
368 redef fun render_latex(v) do
369 v.add_raw "\\includegraphics\{"
370 v.add_text destination
371 v.add_raw "\}"
372 end
373
374 private fun alt_text: String do
375 var v = new RawTextVisitor
376 return v.render(self)
377 end
378 end
379
380 redef class MdLink
381 redef fun render_latex(v) do
382 if is_autolink then
383 v.add_raw "\\url\{"
384 v.add_text destination
385 v.add_raw "\}"
386 return
387 end
388 var title = self.title
389 v.add_raw "\\href\{"
390 v.add_text destination
391 v.add_raw "\}\{"
392 visit_all(v)
393 if title != null and not title.is_empty then
394 v.add_raw " ("
395 v.add_text title
396 v.add_raw ")"
397 end
398 v.add_raw "\}"
399 end
400 end
401
402 redef class MdText
403 redef fun render_latex(v) do
404 v.add_text literal
405 end
406 end
407
408 # Github mode
409
410 redef class MdStrike
411 redef fun render_latex(v) do
412 v.add_raw "\\sout\{"
413 visit_all(v)
414 v.add_raw "\}"
415 end
416 end
417
418 redef class MdSuper
419 redef fun render_latex(v) do
420 v.add_raw "\\textsuperscript\{"
421 visit_all(v)
422 v.add_raw "\}"
423 end
424 end
425
426 # Wikilinks
427
428 redef class MdWikilink
429 redef fun render_latex(v) do
430 v.add_raw "\\texttt\{"
431 var title = self.title
432 if title != null then
433 v.add_text "{title} | "
434 end
435 v.add_text link
436 v.add_raw "\}"
437 end
438 end