bean-kramdown 0.13.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/AUTHORS +1 -0
  2. data/CONTRIBUTERS +11 -0
  3. data/COPYING +24 -0
  4. data/ChangeLog +6683 -0
  5. data/GPL +674 -0
  6. data/README +43 -0
  7. data/VERSION +1 -0
  8. data/bin/kramdown +78 -0
  9. data/lib/kramdown.rb +23 -0
  10. data/lib/kramdown/compatibility.rb +49 -0
  11. data/lib/kramdown/converter.rb +41 -0
  12. data/lib/kramdown/converter/base.rb +169 -0
  13. data/lib/kramdown/converter/bean_html.rb +71 -0
  14. data/lib/kramdown/converter/html.rb +411 -0
  15. data/lib/kramdown/converter/kramdown.rb +428 -0
  16. data/lib/kramdown/converter/latex.rb +607 -0
  17. data/lib/kramdown/converter/toc.rb +82 -0
  18. data/lib/kramdown/document.rb +119 -0
  19. data/lib/kramdown/element.rb +524 -0
  20. data/lib/kramdown/error.rb +30 -0
  21. data/lib/kramdown/options.rb +373 -0
  22. data/lib/kramdown/parser.rb +39 -0
  23. data/lib/kramdown/parser/base.rb +136 -0
  24. data/lib/kramdown/parser/bean_kramdown.rb +25 -0
  25. data/lib/kramdown/parser/bean_kramdown/info_box.rb +52 -0
  26. data/lib/kramdown/parser/bean_kramdown/oembed.rb +230 -0
  27. data/lib/kramdown/parser/html.rb +570 -0
  28. data/lib/kramdown/parser/kramdown.rb +339 -0
  29. data/lib/kramdown/parser/kramdown/abbreviation.rb +71 -0
  30. data/lib/kramdown/parser/kramdown/autolink.rb +53 -0
  31. data/lib/kramdown/parser/kramdown/blank_line.rb +43 -0
  32. data/lib/kramdown/parser/kramdown/block_boundary.rb +46 -0
  33. data/lib/kramdown/parser/kramdown/blockquote.rb +51 -0
  34. data/lib/kramdown/parser/kramdown/codeblock.rb +63 -0
  35. data/lib/kramdown/parser/kramdown/codespan.rb +56 -0
  36. data/lib/kramdown/parser/kramdown/emphasis.rb +70 -0
  37. data/lib/kramdown/parser/kramdown/eob.rb +39 -0
  38. data/lib/kramdown/parser/kramdown/escaped_chars.rb +38 -0
  39. data/lib/kramdown/parser/kramdown/extensions.rb +204 -0
  40. data/lib/kramdown/parser/kramdown/footnote.rb +74 -0
  41. data/lib/kramdown/parser/kramdown/header.rb +68 -0
  42. data/lib/kramdown/parser/kramdown/horizontal_rule.rb +39 -0
  43. data/lib/kramdown/parser/kramdown/html.rb +169 -0
  44. data/lib/kramdown/parser/kramdown/html_entity.rb +44 -0
  45. data/lib/kramdown/parser/kramdown/image.rb +157 -0
  46. data/lib/kramdown/parser/kramdown/line_break.rb +38 -0
  47. data/lib/kramdown/parser/kramdown/link.rb +154 -0
  48. data/lib/kramdown/parser/kramdown/list.rb +240 -0
  49. data/lib/kramdown/parser/kramdown/math.rb +65 -0
  50. data/lib/kramdown/parser/kramdown/paragraph.rb +63 -0
  51. data/lib/kramdown/parser/kramdown/smart_quotes.rb +214 -0
  52. data/lib/kramdown/parser/kramdown/table.rb +178 -0
  53. data/lib/kramdown/parser/kramdown/typographic_symbol.rb +52 -0
  54. data/lib/kramdown/parser/markdown.rb +69 -0
  55. data/lib/kramdown/utils.rb +42 -0
  56. data/lib/kramdown/utils/entities.rb +348 -0
  57. data/lib/kramdown/utils/html.rb +85 -0
  58. data/lib/kramdown/utils/ordered_hash.rb +100 -0
  59. data/lib/kramdown/version.rb +28 -0
  60. metadata +140 -0
@@ -0,0 +1,607 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2012 Thomas Leitner <t_leitner@gmx.at>
5
+ #
6
+ # This file is part of kramdown.
7
+ #
8
+ # kramdown is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation, either version 3 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
20
+ #++
21
+ #
22
+
23
+ require 'set'
24
+
25
+ module Kramdown
26
+
27
+ module Converter
28
+
29
+ # Converts an element tree to LaTeX.
30
+ #
31
+ # This converter uses ideas from other Markdown-to-LaTeX converters like Pandoc and Maruku.
32
+ #
33
+ # You can customize this converter by sub-classing it and overriding the +convert_NAME+ methods.
34
+ # Each such method takes the following parameters:
35
+ #
36
+ # [+el+] The element of type +NAME+ to be converted.
37
+ #
38
+ # [+opts+] A hash containing processing options that are passed down from parent elements. The
39
+ # key :parent is always set and contains the parent element as value.
40
+ #
41
+ # The return value of such a method has to be a string containing the element +el+ formatted
42
+ # correctly as LaTeX markup.
43
+ class Latex < Base
44
+
45
+ # Initialize the LaTeX converter with the +root+ element and the conversion +options+.
46
+ def initialize(root, options)
47
+ super
48
+ #TODO: set the footnote counter at the beginning of the document
49
+ @options[:footnote_nr]
50
+ @data[:packages] = Set.new
51
+ end
52
+
53
+ # Dispatch the conversion of the element +el+ to a +convert_TYPE+ method using the +type+ of
54
+ # the element.
55
+ def convert(el, opts = {})
56
+ send("convert_#{el.type}", el, opts)
57
+ end
58
+
59
+ # Return the converted content of the children of +el+ as a string.
60
+ def inner(el, opts)
61
+ result = ''
62
+ options = opts.dup.merge(:parent => el)
63
+ el.children.each_with_index do |inner_el, index|
64
+ options[:index] = index
65
+ options[:result] = result
66
+ result << send("convert_#{inner_el.type}", inner_el, options)
67
+ end
68
+ result
69
+ end
70
+
71
+ def convert_root(el, opts)
72
+ inner(el, opts)
73
+ end
74
+
75
+ def convert_blank(el, opts)
76
+ opts[:result] =~ /\n\n\Z|\A\Z/ ? "" : "\n"
77
+ end
78
+
79
+ def convert_text(el, opts)
80
+ escape(el.value)
81
+ end
82
+
83
+ def convert_p(el, opts)
84
+ if el.children.size == 1 && el.children.first.type == :img && !(img = convert_img(el.children.first, opts)).empty?
85
+ convert_standalone_image(el, opts, img)
86
+ else
87
+ "#{latex_link_target(el)}#{inner(el, opts)}\n\n"
88
+ end
89
+ end
90
+
91
+ # Helper method used by +convert_p+ to convert a paragraph that only contains a single :img
92
+ # element.
93
+ def convert_standalone_image(el, opts, img)
94
+ attrs = attribute_list(el)
95
+ "\\begin{figure}#{attrs}\n\\begin{center}\n#{img}\n\\end{center}\n\\caption{#{escape(el.children.first.attr['alt'])}}\n#{latex_link_target(el, true)}\n\\end{figure}#{attrs}\n"
96
+ end
97
+
98
+ def convert_codeblock(el, opts)
99
+ show_whitespace = el.attr['class'].to_s =~ /\bshow-whitespaces\b/
100
+ lang = el.attr['lang']
101
+ if show_whitespace || lang
102
+ options = []
103
+ options << "showspaces=%s,showtabs=%s" % (show_whitespace ? ['true', 'true'] : ['false', 'false'])
104
+ options << "language=#{lang}" if lang
105
+ options << "basicstyle=\\ttfamily\\footnotesize,columns=fixed,frame=tlbr"
106
+ id = el.attr['id']
107
+ options << "label=#{id}" if id
108
+ attrs = attribute_list(el)
109
+ "#{latex_link_target(el)}\\begin{lstlisting}[#{options.join(',')}]\n#{el.value}\n\\end{lstlisting}#{attrs}\n"
110
+ else
111
+ "#{latex_link_target(el)}\\begin{verbatim}#{el.value}\\end{verbatim}\n"
112
+ end
113
+ end
114
+
115
+ def convert_blockquote(el, opts)
116
+ latex_environment(el.children.size > 1 ? 'quotation' : 'quote', el, inner(el, opts))
117
+ end
118
+
119
+ def convert_header(el, opts)
120
+ type = @options[:latex_headers][el.options[:level] - 1]
121
+ if ((id = el.attr['id']) ||
122
+ (@options[:auto_ids] && (id = generate_id(el.options[:raw_text])))) && in_toc?(el)
123
+ "\\#{type}{#{inner(el, opts)}}\\hypertarget{#{id}}{}\\label{#{id}}\n\n"
124
+ else
125
+ "\\#{type}*{#{inner(el, opts)}}\n\n"
126
+ end
127
+ end
128
+
129
+ def convert_hr(el, opts)
130
+ attrs = attribute_list(el)
131
+ "#{latex_link_target(el)}\\begin{center}#{attrs}\n\\rule{3in}{0.4pt}\n\\end{center}#{attrs}\n"
132
+ end
133
+
134
+ def convert_ul(el, opts)
135
+ if !@data[:has_toc] && (el.options[:ial][:refs].include?('toc') rescue nil)
136
+ @data[:has_toc] = true
137
+ '\tableofcontents'
138
+ else
139
+ latex_environment(el.type == :ul ? 'itemize' : 'enumerate', el, inner(el, opts))
140
+ end
141
+ end
142
+ alias :convert_ol :convert_ul
143
+
144
+ def convert_dl(el, opts)
145
+ latex_environment('description', el, inner(el, opts))
146
+ end
147
+
148
+ def convert_li(el, opts)
149
+ "\\item #{latex_link_target(el, true)}#{inner(el, opts).sub(/\n+\Z/, '')}\n"
150
+ end
151
+
152
+ def convert_dt(el, opts)
153
+ "\\item[#{inner(el, opts)}] "
154
+ end
155
+
156
+ def convert_dd(el, opts)
157
+ "#{latex_link_target(el)}#{inner(el, opts)}\n\n"
158
+ end
159
+
160
+ def convert_html_element(el, opts)
161
+ if el.value == 'i'
162
+ "\\emph{#{inner(el, opts)}}"
163
+ elsif el.value == 'b'
164
+ "\\emph{#{inner(el, opts)}}"
165
+ else
166
+ warning("Can't convert HTML element")
167
+ ''
168
+ end
169
+ end
170
+
171
+ def convert_xml_comment(el, opts)
172
+ el.value.split(/\n/).map {|l| "% #{l}"}.join("\n") + "\n"
173
+ end
174
+
175
+ def convert_xml_pi(el, opts)
176
+ warning("Can't convert XML PI")
177
+ ''
178
+ end
179
+
180
+ TABLE_ALIGNMENT_CHAR = {:default => 'l', :left => 'l', :center => 'c', :right => 'r'} # :nodoc:
181
+
182
+ def convert_table(el, opts)
183
+ @data[:packages] << 'longtable'
184
+ align = el.options[:alignment].map {|a| TABLE_ALIGNMENT_CHAR[a]}.join('|')
185
+ attrs = attribute_list(el)
186
+ "#{latex_link_target(el)}\\begin{longtable}{|#{align}|}#{attrs}\n\\hline\n#{inner(el, opts)}\\hline\n\\end{longtable}#{attrs}\n\n"
187
+ end
188
+
189
+ def convert_thead(el, opts)
190
+ "#{inner(el, opts)}\\hline\n"
191
+ end
192
+
193
+ def convert_tbody(el, opts)
194
+ inner(el, opts)
195
+ end
196
+
197
+ def convert_tfoot(el, opts)
198
+ "\\hline \\hline \n#{inner(el, opts)}"
199
+ end
200
+
201
+ def convert_tr(el, opts)
202
+ el.children.map {|c| send("convert_#{c.type}", c, opts)}.join(' & ') + "\\\\\n"
203
+ end
204
+
205
+ def convert_td(el, opts)
206
+ inner(el, opts)
207
+ end
208
+
209
+ def convert_comment(el, opts)
210
+ el.value.split(/\n/).map {|l| "% #{l}"}.join("\n") + "\n"
211
+ end
212
+
213
+ def convert_br(el, opts)
214
+ res = "\\newline"
215
+ res << "\n" if (c = opts[:parent].children[opts[:index]+1]) && (c.type != :text || c.value !~ /^\s*\n/)
216
+ res
217
+ end
218
+
219
+ def convert_a(el, opts)
220
+ url = el.attr['href']
221
+ if url =~ /^#/
222
+ "\\hyperlink{#{url[1..-1]}}{#{inner(el, opts)}}"
223
+ else
224
+ "\\href{#{url}}{#{inner(el, opts)}}"
225
+ end
226
+ end
227
+
228
+ def convert_img(el, opts)
229
+ if el.attr['src'] =~ /^(https?|ftps?):\/\//
230
+ warning("Cannot include non-local image")
231
+ ''
232
+ elsif !el.attr['src'].empty?
233
+ @data[:packages] << 'graphicx'
234
+ "#{latex_link_target(el)}\\includegraphics{#{el.attr['src']}}"
235
+ else
236
+ warning("Cannot include image with empty path")
237
+ ''
238
+ end
239
+ end
240
+
241
+ def convert_codespan(el, opts)
242
+ "{\\tt #{latex_link_target(el)}#{escape(el.value)}}"
243
+ end
244
+
245
+ def convert_footnote(el, opts)
246
+ @data[:packages] << 'fancyvrb'
247
+ "\\footnote{#{inner(el.value, opts).rstrip}}"
248
+ end
249
+
250
+ def convert_raw(el, opts)
251
+ if !el.options[:type] || el.options[:type].empty? || el.options[:type].include?('latex')
252
+ el.value + (el.options[:category] == :block ? "\n" : '')
253
+ else
254
+ ''
255
+ end
256
+ end
257
+
258
+ def convert_em(el, opts)
259
+ "\\emph{#{latex_link_target(el)}#{inner(el, opts)}}"
260
+ end
261
+
262
+ def convert_strong(el, opts)
263
+ "\\textbf{#{latex_link_target(el)}#{inner(el, opts)}}"
264
+ end
265
+
266
+ # Inspired by Maruku: entity conversion table based on the one from htmltolatex
267
+ # (http://sourceforge.net/projects/htmltolatex/), with some small adjustments/additions
268
+ ENTITY_CONV_TABLE = {
269
+ 913 => ['$A$'],
270
+ 914 => ['$B$'],
271
+ 915 => ['$\Gamma$'],
272
+ 916 => ['$\Delta$'],
273
+ 917 => ['$E$'],
274
+ 918 => ['$Z$'],
275
+ 919 => ['$H$'],
276
+ 920 => ['$\Theta$'],
277
+ 921 => ['$I$'],
278
+ 922 => ['$K$'],
279
+ 923 => ['$\Lambda$'],
280
+ 924 => ['$M$'],
281
+ 925 => ['$N$'],
282
+ 926 => ['$\Xi$'],
283
+ 927 => ['$O$'],
284
+ 928 => ['$\Pi$'],
285
+ 929 => ['$P$'],
286
+ 931 => ['$\Sigma$'],
287
+ 932 => ['$T$'],
288
+ 933 => ['$Y$'],
289
+ 934 => ['$\Phi$'],
290
+ 935 => ['$X$'],
291
+ 936 => ['$\Psi$'],
292
+ 937 => ['$\Omega$'],
293
+ 945 => ['$\alpha$'],
294
+ 946 => ['$\beta$'],
295
+ 947 => ['$\gamma$'],
296
+ 948 => ['$\delta$'],
297
+ 949 => ['$\epsilon$'],
298
+ 950 => ['$\zeta$'],
299
+ 951 => ['$\eta$'],
300
+ 952 => ['$\theta$'],
301
+ 953 => ['$\iota$'],
302
+ 954 => ['$\kappa$'],
303
+ 955 => ['$\lambda$'],
304
+ 956 => ['$\mu$'],
305
+ 957 => ['$\nu$'],
306
+ 958 => ['$\xi$'],
307
+ 959 => ['$o$'],
308
+ 960 => ['$\pi$'],
309
+ 961 => ['$\rho$'],
310
+ 963 => ['$\sigma$'],
311
+ 964 => ['$\tau$'],
312
+ 965 => ['$\upsilon$'],
313
+ 966 => ['$\phi$'],
314
+ 967 => ['$\chi$'],
315
+ 968 => ['$\psi$'],
316
+ 969 => ['$\omega$'],
317
+ 962 => ['$\varsigma$'],
318
+ 977 => ['$\vartheta$'],
319
+ 982 => ['$\varpi$'],
320
+ 8230 => ['\ldots'],
321
+ 8242 => ['$\prime$'],
322
+ 8254 => ['-'],
323
+ 8260 => ['/'],
324
+ 8472 => ['$\wp$'],
325
+ 8465 => ['$\Im$'],
326
+ 8476 => ['$\Re$'],
327
+ 8501 => ['$\aleph$'],
328
+ 8226 => ['$\bullet$'],
329
+ 8482 => ['$^{\rm TM}$'],
330
+ 8592 => ['$\leftarrow$'],
331
+ 8594 => ['$\rightarrow$'],
332
+ 8593 => ['$\uparrow$'],
333
+ 8595 => ['$\downarrow$'],
334
+ 8596 => ['$\leftrightarrow$'],
335
+ 8629 => ['$\hookleftarrow$'],
336
+ 8657 => ['$\Uparrow$'],
337
+ 8659 => ['$\Downarrow$'],
338
+ 8656 => ['$\Leftarrow$'],
339
+ 8658 => ['$\Rightarrow$'],
340
+ 8660 => ['$\Leftrightarrow$'],
341
+ 8704 => ['$\forall$'],
342
+ 8706 => ['$\partial$'],
343
+ 8707 => ['$\exists$'],
344
+ 8709 => ['$\emptyset$'],
345
+ 8711 => ['$\nabla$'],
346
+ 8712 => ['$\in$'],
347
+ 8715 => ['$\ni$'],
348
+ 8713 => ['$\notin$'],
349
+ 8721 => ['$\sum$'],
350
+ 8719 => ['$\prod$'],
351
+ 8722 => ['$-$'],
352
+ 8727 => ['$\ast$'],
353
+ 8730 => ['$\surd$'],
354
+ 8733 => ['$\propto$'],
355
+ 8734 => ['$\infty$'],
356
+ 8736 => ['$\angle$'],
357
+ 8743 => ['$\wedge$'],
358
+ 8744 => ['$\vee$'],
359
+ 8745 => ['$\cup$'],
360
+ 8746 => ['$\cap$'],
361
+ 8747 => ['$\int$'],
362
+ 8756 => ['$\therefore$', 'amssymb'],
363
+ 8764 => ['$\sim$'],
364
+ 8776 => ['$\approx$'],
365
+ 8773 => ['$\cong$'],
366
+ 8800 => ['$\neq$'],
367
+ 8801 => ['$\equiv$'],
368
+ 8804 => ['$\leq$'],
369
+ 8805 => ['$\geq$'],
370
+ 8834 => ['$\subset$'],
371
+ 8835 => ['$\supset$'],
372
+ 8838 => ['$\subseteq$'],
373
+ 8839 => ['$\supseteq$'],
374
+ 8836 => ['$\nsubset$', 'amssymb'],
375
+ 8853 => ['$\oplus$'],
376
+ 8855 => ['$\otimes$'],
377
+ 8869 => ['$\perp$'],
378
+ 8901 => ['$\cdot$'],
379
+ 8968 => ['$\rceil$'],
380
+ 8969 => ['$\lceil$'],
381
+ 8970 => ['$\lfloor$'],
382
+ 8971 => ['$\rfloor$'],
383
+ 9001 => ['$\rangle$'],
384
+ 9002 => ['$\langle$'],
385
+ 9674 => ['$\lozenge$', 'amssymb'],
386
+ 9824 => ['$\spadesuit$'],
387
+ 9827 => ['$\clubsuit$'],
388
+ 9829 => ['$\heartsuit$'],
389
+ 9830 => ['$\diamondsuit$'],
390
+ 38 => ['\&'],
391
+ 34 => ['"'],
392
+ 39 => ['\''],
393
+ 169 => ['\copyright'],
394
+ 60 => ['\textless'],
395
+ 62 => ['\textgreater'],
396
+ 338 => ['\OE'],
397
+ 339 => ['\oe'],
398
+ 352 => ['\v{S}'],
399
+ 353 => ['\v{s}'],
400
+ 376 => ['\"Y'],
401
+ 710 => ['\textasciicircum'],
402
+ 732 => ['\textasciitilde'],
403
+ 8211 => ['--'],
404
+ 8212 => ['---'],
405
+ 8216 => ['`'],
406
+ 8217 => ['\''],
407
+ 8220 => ['``'],
408
+ 8221 => ['\'\''],
409
+ 8224 => ['\dag'],
410
+ 8225 => ['\ddag'],
411
+ 8240 => ['\permil', 'wasysym'],
412
+ 8364 => ['\euro', 'eurosym'],
413
+ 8249 => ['\guilsinglleft'],
414
+ 8250 => ['\guilsinglright'],
415
+ 8218 => ['\quotesinglbase', 'mathcomp'],
416
+ 8222 => ['\quotedblbase', 'mathcomp'],
417
+ 402 => ['\textflorin', 'mathcomp'],
418
+ 381 => ['\v{Z}'],
419
+ 382 => ['\v{z}'],
420
+ 160 => ['\nolinebreak'],
421
+ 161 => ['\textexclamdown'],
422
+ 163 => ['\pounds'],
423
+ 164 => ['\currency', 'wasysym'],
424
+ 165 => ['\textyen', 'textcomp'],
425
+ 166 => ['\brokenvert', 'wasysym'],
426
+ 167 => ['\S'],
427
+ 171 => ['\guillemotleft'],
428
+ 187 => ['\guillemotright'],
429
+ 174 => ['\textregistered'],
430
+ 170 => ['\textordfeminine'],
431
+ 172 => ['$\neg$'],
432
+ 176 => ['$\degree$', 'mathabx'],
433
+ 177 => ['$\pm$'],
434
+ 180 => ['\''],
435
+ 181 => ['$\mu$'],
436
+ 182 => ['\P'],
437
+ 183 => ['$\cdot$'],
438
+ 186 => ['\textordmasculine'],
439
+ 162 => ['\cent', 'wasysym'],
440
+ 185 => ['$^1$'],
441
+ 178 => ['$^2$'],
442
+ 179 => ['$^3$'],
443
+ 189 => ['$\frac{1}{2}$'],
444
+ 188 => ['$\frac{1}{4}$'],
445
+ 190 => ['$\frac{3}{4}'],
446
+ 192 => ['\`A'],
447
+ 193 => ['\\\'A'],
448
+ 194 => ['\^A'],
449
+ 195 => ['\~A'],
450
+ 196 => ['\"A'],
451
+ 197 => ['\AA'],
452
+ 198 => ['\AE'],
453
+ 199 => ['\cC'],
454
+ 200 => ['\`E'],
455
+ 201 => ['\\\'E'],
456
+ 202 => ['\^E'],
457
+ 203 => ['\"E'],
458
+ 204 => ['\`I'],
459
+ 205 => ['\\\'I'],
460
+ 206 => ['\^I'],
461
+ 207 => ['\"I'],
462
+ 208 => ['$\eth$', 'amssymb'],
463
+ 209 => ['\~N'],
464
+ 210 => ['\`O'],
465
+ 211 => ['\\\'O'],
466
+ 212 => ['\^O'],
467
+ 213 => ['\~O'],
468
+ 214 => ['\"O'],
469
+ 215 => ['$\times$'],
470
+ 216 => ['\O'],
471
+ 217 => ['\`U'],
472
+ 218 => ['\\\'U'],
473
+ 219 => ['\^U'],
474
+ 220 => ['\"U'],
475
+ 221 => ['\\\'Y'],
476
+ 222 => ['\Thorn', 'wasysym'],
477
+ 223 => ['\ss'],
478
+ 224 => ['\`a'],
479
+ 225 => ['\\\'a'],
480
+ 226 => ['\^a'],
481
+ 227 => ['\~a'],
482
+ 228 => ['\"a'],
483
+ 229 => ['\aa'],
484
+ 230 => ['\ae'],
485
+ 231 => ['\cc'],
486
+ 232 => ['\`e'],
487
+ 233 => ['\\\'e'],
488
+ 234 => ['\^e'],
489
+ 235 => ['\"e'],
490
+ 236 => ['\`i'],
491
+ 237 => ['\\\'i'],
492
+ 238 => ['\^i'],
493
+ 239 => ['\"i'],
494
+ 240 => ['$\eth$'],
495
+ 241 => ['\~n'],
496
+ 242 => ['\`o'],
497
+ 243 => ['\\\'o'],
498
+ 244 => ['\^o'],
499
+ 245 => ['\~o'],
500
+ 246 => ['\"o'],
501
+ 247 => ['$\divide$'],
502
+ 248 => ['\o'],
503
+ 249 => ['\`u'],
504
+ 250 => ['\\\'u'],
505
+ 251 => ['\^u'],
506
+ 252 => ['\"u'],
507
+ 253 => ['\\\'y'],
508
+ 254 => ['\thorn', 'wasysym'],
509
+ 255 => ['\"y'],
510
+ 8201 => ['\thinspace'],
511
+ 8194 => ['\hskip .5em\relax'],
512
+ 8195 => ['\quad'],
513
+ } # :nodoc:
514
+ ENTITY_CONV_TABLE.each {|k,v| ENTITY_CONV_TABLE[k][0].insert(-1, '{}')}
515
+
516
+ def entity_to_latex(entity)
517
+ text, package = ENTITY_CONV_TABLE[entity.code_point]
518
+ if text
519
+ @data[:packages] << package if package
520
+ text
521
+ else
522
+ warning("Couldn't find entity with code #{entity.code_point} in substitution table!")
523
+ ''
524
+ end
525
+ end
526
+
527
+ def convert_entity(el, opts)
528
+ entity_to_latex(el.value)
529
+ end
530
+
531
+ TYPOGRAPHIC_SYMS = {
532
+ :mdash => '---', :ndash => '--', :hellip => '\ldots{}',
533
+ :laquo_space => '\guillemotleft{}~', :raquo_space => '~\guillemotright{}',
534
+ :laquo => '\guillemotleft{}', :raquo => '\guillemotright{}'
535
+ } # :nodoc:
536
+ def convert_typographic_sym(el, opts)
537
+ TYPOGRAPHIC_SYMS[el.value]
538
+ end
539
+
540
+ def convert_smart_quote(el, opts)
541
+ res = entity_to_latex(smart_quote_entity(el)).chomp('{}')
542
+ res << "{}" if ((nel = opts[:parent].children[opts[:index]+1]) && nel.type == :smart_quote) || res =~ /\w$/
543
+ res
544
+ end
545
+
546
+ def convert_math(el, opts)
547
+ @data[:packages] += %w[amssymb amsmath amsthm amsfonts]
548
+ if el.options[:category] == :block
549
+ if el.value =~ /\A\s*\\begin\{/
550
+ el.value
551
+ else
552
+ latex_environment('displaymath', el, el.value)
553
+ end
554
+ else
555
+ "$#{el.value}$"
556
+ end
557
+ end
558
+
559
+ def convert_abbreviation(el, opts)
560
+ el.value
561
+ end
562
+
563
+ # Wrap the +text+ inside a LaTeX environment of type +type+. The element +el+ is passed on to
564
+ # the method #attribute_list -- the resulting string is appended to both the \\begin and the
565
+ # \\end lines of the LaTeX environment for easier post-processing of LaTeX environments.
566
+ def latex_environment(type, el, text)
567
+ attrs = attribute_list(el)
568
+ "\\begin{#{type}}#{latex_link_target(el)}#{attrs}\n#{text.rstrip}\n\\end{#{type}}#{attrs}\n"
569
+ end
570
+
571
+ # Return a string containing a valid \hypertarget command if the element has an ID defined, or
572
+ # +nil+ otherwise. If the parameter +add_label+ is +true+, a \label command will also be used
573
+ # additionally to the \hypertarget command.
574
+ def latex_link_target(el, add_label = false)
575
+ if (id = el.attr['id'])
576
+ "\\hypertarget{#{id}}{}" << (add_label ? "\\label{#{id}}" : '')
577
+ else
578
+ nil
579
+ end
580
+ end
581
+
582
+ # Return a LaTeX comment containing all attributes as 'key="value"' pairs.
583
+ def attribute_list(el)
584
+ attrs = el.attr.map {|k,v| v.nil? ? '' : " #{k}=\"#{v.to_s}\""}.compact.sort.join('')
585
+ attrs = " % #{attrs}" if !attrs.empty?
586
+ attrs
587
+ end
588
+
589
+ ESCAPE_MAP = {
590
+ "^" => "\\^{}",
591
+ "\\" => "\\textbackslash{}",
592
+ "~" => "\\ensuremath{\\sim}",
593
+ "|" => "\\textbar{}",
594
+ "<" => "\\textless{}",
595
+ ">" => "\\textgreater{}"
596
+ }.merge(Hash[*("{}$%&_#".scan(/./).map {|c| [c, "\\#{c}"]}.flatten)]) # :nodoc:
597
+ ESCAPE_RE = Regexp.union(*ESCAPE_MAP.collect {|k,v| k}) # :nodoc:
598
+
599
+ # Escape the special LaTeX characters in the string +str+.
600
+ def escape(str)
601
+ str.gsub(ESCAPE_RE) {|m| ESCAPE_MAP[m]}
602
+ end
603
+
604
+ end
605
+
606
+ end
607
+ end