bean-kramdown 0.13.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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