kramdown 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of kramdown might be problematic. Click here for more details.

Files changed (69) hide show
  1. data/CONTRIBUTERS +2 -1
  2. data/ChangeLog +454 -0
  3. data/README +1 -1
  4. data/VERSION +1 -1
  5. data/doc/{default.less.css → default.scss.css} +12 -9
  6. data/doc/documentation.page +1 -1
  7. data/doc/index.page +37 -3
  8. data/doc/syntax.page +10 -7
  9. data/lib/kramdown/converter.rb +1 -0
  10. data/lib/kramdown/converter/html.rb +61 -67
  11. data/lib/kramdown/converter/kramdown.rb +398 -0
  12. data/lib/kramdown/converter/latex.rb +274 -276
  13. data/lib/kramdown/document.rb +2 -0
  14. data/lib/kramdown/options.rb +16 -15
  15. data/lib/kramdown/parser/base.rb +0 -1
  16. data/lib/kramdown/parser/html.rb +122 -27
  17. data/lib/kramdown/parser/kramdown.rb +2 -0
  18. data/lib/kramdown/parser/kramdown/attribute_list.rb +2 -0
  19. data/lib/kramdown/parser/kramdown/extension.rb +2 -1
  20. data/lib/kramdown/parser/kramdown/html.rb +2 -2
  21. data/lib/kramdown/parser/kramdown/html_entity.rb +1 -1
  22. data/lib/kramdown/parser/kramdown/link.rb +5 -2
  23. data/lib/kramdown/parser/kramdown/list.rb +13 -9
  24. data/lib/kramdown/parser/kramdown/math.rb +1 -1
  25. data/lib/kramdown/parser/kramdown/typographic_symbol.rb +4 -4
  26. data/lib/kramdown/utils.rb +36 -0
  27. data/lib/kramdown/utils/entities.rb +338 -0
  28. data/lib/kramdown/utils/html.rb +72 -0
  29. data/lib/kramdown/version.rb +1 -1
  30. data/man/man1/kramdown.1 +20 -17
  31. data/test/run_tests.rb +1 -1
  32. data/test/test_files.rb +60 -3
  33. data/test/testcases/block/06_codeblock/whitespace.html +2 -2
  34. data/test/testcases/block/07_horizontal_rule/error.html.19 +7 -0
  35. data/test/testcases/block/09_html/html_to_native/code.html +1 -1
  36. data/test/testcases/block/09_html/html_to_native/code.text +1 -1
  37. data/test/testcases/block/09_html/html_to_native/table_simple.html +38 -2
  38. data/test/testcases/block/09_html/html_to_native/table_simple.text +42 -0
  39. data/test/testcases/block/09_html/html_to_native/typography.html.19 +1 -0
  40. data/test/testcases/block/09_html/simple.html.19 +62 -0
  41. data/test/testcases/block/11_ial/simple.html +3 -2
  42. data/test/testcases/block/12_extension/comment.html +3 -2
  43. data/test/testcases/block/12_extension/options.html +0 -3
  44. data/test/testcases/block/12_extension/options.text +0 -6
  45. data/test/testcases/block/13_definition_list/item_ial.html +14 -0
  46. data/test/testcases/block/13_definition_list/item_ial.text +8 -0
  47. data/test/testcases/block/16_toc/no_toc_depth.html +33 -0
  48. data/test/testcases/block/16_toc/no_toc_depth.options +1 -0
  49. data/test/testcases/block/16_toc/no_toc_depth.text +16 -0
  50. data/test/testcases/block/16_toc/toc_depth_2.html +24 -0
  51. data/test/testcases/block/16_toc/toc_depth_2.options +1 -0
  52. data/test/testcases/block/16_toc/toc_depth_2.text +16 -0
  53. data/test/testcases/span/01_link/empty.html +2 -0
  54. data/test/testcases/span/01_link/empty.text +2 -0
  55. data/test/testcases/span/01_link/imagelinks.html +2 -0
  56. data/test/testcases/span/01_link/imagelinks.text +2 -0
  57. data/test/testcases/span/01_link/inline.html.19 +40 -0
  58. data/test/testcases/span/01_link/reference.html.19 +32 -0
  59. data/test/testcases/span/extension/comment.html +3 -3
  60. data/test/testcases/span/text_substitutions/entities.html.19 +4 -0
  61. data/test/testcases/span/text_substitutions/entities_numeric.html +1 -0
  62. data/test/testcases/span/text_substitutions/entities_numeric.html.19 +1 -0
  63. data/test/testcases/span/text_substitutions/entities_numeric.options +1 -0
  64. data/test/testcases/span/text_substitutions/entities_numeric.text +1 -0
  65. data/test/testcases/span/text_substitutions/typography.html.19 +18 -0
  66. metadata +30 -10
  67. data/test/testcases/block/09_html/filtered_html.html +0 -1
  68. data/test/testcases/block/09_html/filtered_html.options +0 -1
  69. data/test/testcases/block/09_html/filtered_html.text +0 -1
@@ -0,0 +1,398 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2010 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 'rexml/parsers/baseparser'
24
+
25
+ module Kramdown
26
+
27
+ module Converter
28
+
29
+ # Converts a Kramdown::Document to the kramdown format.
30
+ class Kramdown < Base
31
+
32
+ # :stopdoc:
33
+
34
+ include ::Kramdown::Utils::HTML
35
+
36
+ def initialize(doc)
37
+ super
38
+ @linkrefs = []
39
+ @footnotes = []
40
+ @abbrevs = []
41
+ @stack = []
42
+ end
43
+
44
+ def convert(el, opts = {})
45
+ res = send("convert_#{el.type}", el, opts)
46
+ if el.type != :html_element && el.type != :li && el.type != :dd && (ial = ial_for_element(el))
47
+ res << ial
48
+ res << "\n\n" if el.options[:category] == :block
49
+ end
50
+ res
51
+ end
52
+
53
+ def inner(el, opts = {})
54
+ @stack.push([el, opts])
55
+ result = ''
56
+ el.children.each_with_index do |inner_el, index|
57
+ options = opts.dup
58
+ #p [index, inner_el]
59
+ options[:index] = index
60
+ options[:prev] = (index == 0 ? nil : el.children[index-1])
61
+ options[:next] = (index == el.children.length - 1 ? nil : el.children[index+1])
62
+ result << convert(inner_el, options)
63
+ end
64
+ @stack.pop
65
+ result
66
+ end
67
+
68
+ def convert_blank(el, opts)
69
+ "\n"
70
+ end
71
+
72
+ ESCAPED_CHAR_RE = /(\$\$|[\\*_`\[\]\{\}"'])|^[ ]{0,3}(:)/
73
+
74
+ def convert_text(el, opts)
75
+ if opts[:raw_text]
76
+ el.value
77
+ else
78
+ nl = (el.value =~ /\n$/)
79
+ el.value.gsub(/\s+/, ' ').gsub(ESCAPED_CHAR_RE) { "\\#{$1 || $2}" } + (nl ? "\n" : '')
80
+ end
81
+ end
82
+
83
+ def convert_p(el, opts)
84
+ res = inner(el, opts).strip.gsub(/\A(?:([#|])|(\d+)\.|([+-]\s))/) do
85
+ $1 || $3 ? "\\#{$1 || $3}" : "#{$2}\\."
86
+ end + "\n"
87
+ if opts[:next] && opts[:next].type == :p && !ial_for_element(el)
88
+ res += "\n"
89
+ end
90
+ res
91
+ end
92
+
93
+ CODEBLOCK_PREV_EL = [:ul, :ol, :dl, :codeblock]
94
+
95
+ def convert_codeblock(el, opts)
96
+ res = ''
97
+ res << "^\n" if opts[:prev] && ((CODEBLOCK_PREV_EL.include?(opts[:prev].type) && !ial_for_element(opts[:prev])) ||
98
+ (opts[:prev].type == :blank &&
99
+ opts[:index]-2 >= 0 &&
100
+ (tmp = @stack.last.first.children[opts[:index]-2]) &&
101
+ CODEBLOCK_PREV_EL.include?(tmp.type) && !ial_for_element(tmp)))
102
+ res << el.value.split(/\n/).map {|l| l.empty? ? " " : " #{l}"}.join("\n") + "\n"
103
+ end
104
+
105
+ def convert_blockquote(el, opts)
106
+ res = ''
107
+ res << "\n" if opts[:prev] && opts[:prev].type == :blockquote
108
+ res << inner(el, opts).chomp.split(/\n/).map {|l| "> #{l}"}.join("\n") << "\n"
109
+ end
110
+
111
+ def convert_header(el, opts)
112
+ res = ''
113
+ res << "\n" if opts[:prev] && opts[:prev].type != :blank
114
+ res << "#{'#' * el.options[:level]} #{inner(el, opts)}"
115
+ res << " {##{el.options[:attr]['id']}}" if el.options[:attr] && el.options[:attr]['id']
116
+ res << "\n" if opts[:next] && opts[:next].type != :blank
117
+ res << "\n"
118
+ end
119
+
120
+ def convert_hr(el, opts)
121
+ "* * *\n"
122
+ end
123
+
124
+ def convert_ul(el, opts)
125
+ res = ''
126
+ res << "\n" if opts[:prev] && (opts[:prev].type == :p && !opts[:prev].options[:transparent])
127
+ res << "^\n" if opts[:prev] && ((opts[:prev].type == el.type && !ial_for_element(opts[:prev])) ||
128
+ (opts[:prev].type == :blank && opts[:index]-2 >= 0 &&
129
+ (tmp = @stack.last.first.children[opts[:index]-2]) &&
130
+ tmp.type == el.type && !ial_for_element(tmp)))
131
+ res + inner(el, opts).sub(/\n+\Z/, "\n")
132
+ end
133
+ alias :convert_ol :convert_ul
134
+ alias :convert_dl :convert_ul
135
+
136
+ def convert_li(el, opts)
137
+ sym, width = if @stack.last.first.type == :ul
138
+ ['* ', el.children.first.type == :codeblock ? 4 : 2]
139
+ else
140
+ ["#{opts[:index] + 1}.".ljust(4), 4]
141
+ end
142
+ if ial = ial_for_element(el)
143
+ sym += ial + " "
144
+ end
145
+
146
+ first, *last = inner(el, opts).chomp.split(/\n/)
147
+ last = last.map {|l| " "*width + l}.join("\n")
148
+ last = last.empty? ? "\n" : "\n#{last}\n"
149
+ if el.children.first.type == :p && !el.children.first.options[:transparent]
150
+ res = "#{sym}#{first}\n#{last}"
151
+ res << "^\n" if el.children.size == 1 && @stack.last.first.children.last == el &&
152
+ (@stack.last.first.children.any? {|c| c.children.first.type != :p} || @stack.last.first.children.size == 1)
153
+ res
154
+ elsif el.children.first.type == :codeblock
155
+ "#{sym}\n #{first}#{last}"
156
+ else
157
+ "#{sym}#{first}#{last}"
158
+ end
159
+ end
160
+
161
+ def convert_dd(el, opts)
162
+ sym, width = ": ", (el.children.first.type == :codeblock ? 4 : 2)
163
+ if ial = ial_for_element(el)
164
+ sym += ial + " "
165
+ end
166
+
167
+ first, *last = inner(el, opts).chomp.split(/\n/)
168
+ last = last.map {|l| " "*width + l}.join("\n")
169
+ text = first + (last.empty? ? '' : "\n" + last)
170
+ if el.children.first.type == :p && !el.children.first.options[:transparent]
171
+ "\n#{sym}#{text}\n"
172
+ elsif el.children.first.type == :codeblock
173
+ "#{sym}\n #{text}\n"
174
+ else
175
+ "#{sym}#{text}\n"
176
+ end
177
+ end
178
+
179
+ def convert_dt(el, opts)
180
+ res = ''
181
+ res << inner(el, opts) << "\n"
182
+ end
183
+
184
+ HTML_TAGS_WITH_BODY=['div', 'script']
185
+
186
+ def convert_html_element(el, opts)
187
+ markdown_attr = el.options[:category] == :block && el.children.any? do |c|
188
+ c.type != :html_element && (c.type != :p || !c.options[:transparent]) && c.options[:category] == :block
189
+ end
190
+ opts[:force_raw_text] = true if %w{script pre code}.include?(el.value)
191
+ opts[:raw_text] = opts[:force_raw_text] || opts[:block_raw_text] || (el.options[:category] != :span && !markdown_attr)
192
+ opts[:block_raw_text] = true if el.options[:category] == :block && opts[:raw_text]
193
+ res = inner(el, opts)
194
+ if el.options[:category] == :span
195
+ "<#{el.value}#{html_attributes(el)}" << (!res.empty? ? ">#{res}</#{el.value}>" : " />")
196
+ else
197
+ output = ''
198
+ output << "<#{el.value}#{html_attributes(el)}"
199
+ output << " markdown=\"1\"" if markdown_attr
200
+ if !res.empty? && el.options[:parse_type] != :block
201
+ output << ">#{res}</#{el.value}>"
202
+ elsif !res.empty?
203
+ output << ">\n#{res}" << "</#{el.value}>"
204
+ elsif HTML_TAGS_WITH_BODY.include?(el.value)
205
+ output << "></#{el.value}>"
206
+ else
207
+ output << " />"
208
+ end
209
+ output << "\n" if el.options[:outer_element] || !el.options[:parent_is_raw]
210
+ output
211
+ end
212
+ end
213
+
214
+ def convert_xml_comment(el, opts)
215
+ if el.options[:category] == :block && !el.options[:parent_is_raw]
216
+ el.value + "\n"
217
+ else
218
+ el.value
219
+ end
220
+ end
221
+ alias :convert_xml_pi :convert_xml_comment
222
+ alias :convert_html_doctype :convert_xml_comment
223
+
224
+ def convert_table(el, opts)
225
+ opts[:alignment] = el.options[:alignment]
226
+ inner(el, opts)
227
+ end
228
+
229
+ def convert_thead(el, opts)
230
+ rows = inner(el, opts)
231
+ if opts[:alignment].all? {|a| a == :default}
232
+ "#{rows}|" + "-"*10 + "\n"
233
+ else
234
+ "#{rows}| " + opts[:alignment].map do |a|
235
+ case a
236
+ when :left then ":-"
237
+ when :right then "-:"
238
+ when :center then ":-:"
239
+ when :default then "-"
240
+ end
241
+ end.join(' ') + "\n"
242
+ end
243
+ end
244
+
245
+ def convert_tbody(el, opts)
246
+ res = ''
247
+ res << inner(el, opts)
248
+ res << '|' << '-'*10 << "\n" if opts[:next] && opts[:next].type == :tbody
249
+ res
250
+ end
251
+
252
+ def convert_tfoot(el, opts)
253
+ "|" + "="*10 + "\n#{inner(el, opts)}"
254
+ end
255
+
256
+ def convert_tr(el, opts)
257
+ "| " + el.children.map {|c| convert(c, opts)}.join(" | ") + " |\n"
258
+ end
259
+
260
+ def convert_td(el, opts)
261
+ inner(el, opts).gsub(/\|/, '\\|')
262
+ end
263
+ alias :convert_th :convert_td
264
+
265
+ def convert_comment(el, opts)
266
+ if el.options[:category] == :block
267
+ "{::comment}\n#{el.value}\n{:/}\n"
268
+ else
269
+ "{::comment}#{el.value}{:/}"
270
+ end
271
+ end
272
+
273
+ def convert_br(el, opts)
274
+ " \n"
275
+ end
276
+
277
+ def convert_a(el, opts)
278
+ if el.options[:attr]['href'].empty?
279
+ "[#{inner(el, opts)}]()"
280
+ else
281
+ @linkrefs << el
282
+ "[#{inner(el, opts)}][#{@linkrefs.size}]"
283
+ end
284
+ end
285
+
286
+ def convert_img(el, opts)
287
+ title = (el.options[:attr]['title'] ? ' "' + el.options[:attr]['title'].gsub(/"/, "&quot;") + '"' : '')
288
+ "![#{el.options[:attr]['alt']}](<#{el.options[:attr]['src']}>#{title})"
289
+ end
290
+
291
+ def convert_codespan(el, opts)
292
+ delim = (el.value.scan(/`+/).max || '') + '`'
293
+ "#{delim}#{' ' if delim.size > 1}#{el.value}#{' ' if delim.size > 1}#{delim}"
294
+ end
295
+
296
+ def convert_footnote(el, opts)
297
+ @footnotes << [el.options[:name], @doc.parse_infos[:footnotes][el.options[:name]]]
298
+ "[^#{el.options[:name]}]"
299
+ end
300
+
301
+ def convert_raw(el, opts)
302
+ if @stack.last.first.type == :html_element
303
+ el.value
304
+ elsif el.options[:category] == :block
305
+ "{::nomarkdown}\n#{el.value}\n{:/}\n"
306
+ else
307
+ "{::nomarkdown}#{el.value}{:/}"
308
+ end
309
+ end
310
+
311
+ def convert_em(el, opts)
312
+ "*#{inner(el, opts)}*"
313
+ end
314
+
315
+ def convert_strong(el, opts)
316
+ "**#{inner(el, opts)}**"
317
+ end
318
+
319
+ def convert_entity(el, opts)
320
+ entity_to_str(el.value)
321
+ end
322
+
323
+ TYPOGRAPHIC_SYMS = {
324
+ :mdash => '---', :ndash => '--', :hellip => '...',
325
+ :laquo_space => '<< ', :raquo_space => ' >>',
326
+ :laquo => '<<', :raquo => '>>'
327
+ }
328
+ def convert_typographic_sym(el, opts)
329
+ TYPOGRAPHIC_SYMS[el.value]
330
+ end
331
+
332
+ def convert_smart_quote(el, opts)
333
+ el.value.to_s =~ /[rl]dquo/ ? "\"" : "'"
334
+ end
335
+
336
+ def convert_math(el, opts)
337
+ (@stack.last.first.type == :p && opts[:prev].nil? ? "\\" : '') + "$$#{el.value}$$" + (el.options[:category] == :block ? "\n" : '')
338
+ end
339
+
340
+ def convert_abbreviation(el, opts)
341
+ el.value
342
+ end
343
+
344
+ def convert_root(el, opts)
345
+ res = inner(el, opts)
346
+ res << create_link_defs
347
+ res << create_footnote_defs
348
+ res << create_abbrev_defs
349
+ res
350
+ end
351
+
352
+ def create_link_defs
353
+ res = ''
354
+ res << "\n\n" if @linkrefs.size > 0
355
+ @linkrefs.each_with_index do |el, i|
356
+ link = (el.type == :a ? el.options[:attr]['href'] : el.options[:attr]['src'])
357
+ link = "<#{link}>" if link =~ / /
358
+ title = el.options[:attr]['title']
359
+ res << "[#{i+1}]: #{link} #{title ? '"' + title.gsub(/"/, "&quot;") + '"' : ''}\n"
360
+ end
361
+ res
362
+ end
363
+
364
+ def create_footnote_defs
365
+ res = ''
366
+ res = "\n" if @footnotes.size > 0
367
+ @footnotes.each do |name, data|
368
+ res << "\n[^#{name}]:\n"
369
+ res << inner(data[:content]).chomp.split(/\n/).map {|l| " #{l}"}.join("\n")
370
+ end
371
+ res
372
+ end
373
+
374
+ def create_abbrev_defs
375
+ return '' unless @doc.parse_infos[:abbrev_defs]
376
+ res = ''
377
+ @doc.parse_infos[:abbrev_defs].each do |name, text|
378
+ res << "*[#{name}]: #{text}\n"
379
+ end
380
+ res
381
+ end
382
+
383
+ # Return the IAL containing the attributes of the element +el+.
384
+ def ial_for_element(el)
385
+ res = (el.options[:attr] || {}).map do |k,v|
386
+ next if [:img, :a].include?(el.type) && ['href', 'src', 'alt', 'title'].include?(k)
387
+ next if el.type == :header && k == 'id'
388
+ v.nil? ? '' : " #{k}=\"#{v.to_s}\""
389
+ end.compact.sort.join('')
390
+ res = "toc" + (res.strip.empty? ? '' : " #{res}") if (el.type == :ul || el.type == :ol) &&
391
+ (el.options[:ial][:refs].include?('toc') rescue nil)
392
+ res.strip.empty? ? nil : "{:#{res}}"
393
+ end
394
+
395
+ end
396
+
397
+ end
398
+ end
@@ -64,37 +64,29 @@ module Kramdown
64
64
  escape(el.value)
65
65
  end
66
66
 
67
- def convert_eob(el, opts)
68
- ''
69
- end
70
-
71
67
  def convert_p(el, opts)
72
68
  "#{inner(el, opts)}\n\n"
73
69
  end
74
70
 
75
71
  def convert_codeblock(el, opts)
76
- if !el.value
77
- @doc.warnings << "Cannot convert codeblock with entity references"
78
- return ''
79
- end
80
72
  show_whitespace = el.options[:attr] && el.options[:attr]['class'].to_s =~ /\bshow-whitespaces\b/
81
73
  lang = el.options[:attr] && el.options[:attr]['lang']
82
74
  if show_whitespace || lang
83
75
  result = "\\lstset{showspaces=%s,showtabs=%s}\n" % (show_whitespace ? ['true', 'true'] : ['false', 'false'])
84
76
  result += "\\lstset{language=#{lang}}\n" if lang
85
77
  result += "\\lstset{basicstyle=\\ttfamily\\footnotesize}\\lstset{columns=fixed,frame=tlbr}\n"
86
- "#{result}\\begin{lstlisting}\n#{el.value}\n\\end{lstlisting}"
78
+ "#{result}\\begin{lstlisting}#{attribute_list(el)}\n#{el.value}\n\\end{lstlisting}\n"
87
79
  else
88
80
  "\\begin{verbatim}#{el.value}\\end{verbatim}\n"
89
81
  end
90
82
  end
91
83
 
92
- def latex_environment(type, text)
93
- "\\begin{#{type}}\n#{text}\n\\end{#{type}}\n"
84
+ def latex_environment(type, el, text)
85
+ "\\begin{#{type}}#{attribute_list(el)}\n#{text}\n\\end{#{type}}\n"
94
86
  end
95
87
 
96
88
  def convert_blockquote(el, opts)
97
- latex_environment('quote', inner(el, opts))
89
+ latex_environment('quote', el, inner(el, opts))
98
90
  end
99
91
 
100
92
  HEADER_TYPES = {
@@ -107,8 +99,9 @@ module Kramdown
107
99
  }
108
100
  def convert_header(el, opts)
109
101
  type = HEADER_TYPES[el.options[:level]]
110
- if (el.options[:attr] && (id = el.options[:attr]['id'])) ||
111
- (@doc.options[:auto_ids] && (id = generate_id(el.options[:raw_text])))
102
+ if ((el.options[:attr] && (id = el.options[:attr]['id'])) ||
103
+ (@doc.options[:auto_ids] && (id = generate_id(el.options[:raw_text])))) &&
104
+ (@doc.options[:toc_depth] <= 0 || el.options[:level] <= @doc.options[:toc_depth])
112
105
  "\\hypertarget{#{id}}{}\\#{type}{#{inner(el, opts)}}\\label{#{id}}\n\n"
113
106
  else
114
107
  "\\#{type}*{#{inner(el, opts)}}\n\n"
@@ -116,7 +109,7 @@ module Kramdown
116
109
  end
117
110
 
118
111
  def convert_hr(el, opts)
119
- "\\begin{center}\\rule{3in}{0.4pt}\\end{center}\n"
112
+ "\\begin{center}#{attribute_list(el)}\n\\rule{3in}{0.4pt}\n\\end{center}\n"
120
113
  end
121
114
 
122
115
  def convert_ul(el, opts)
@@ -124,17 +117,17 @@ module Kramdown
124
117
  @doc.conversion_infos[:has_toc] = true
125
118
  '\tableofcontents'
126
119
  else
127
- latex_environment(el.type == :ul ? 'itemize' : 'enumerate', inner(el, opts))
120
+ latex_environment(el.type == :ul ? 'itemize' : 'enumerate', el, inner(el, opts))
128
121
  end
129
122
  end
130
123
  alias :convert_ol :convert_ul
131
124
 
132
125
  def convert_dl(el, opts)
133
- latex_environment('description', inner(el, opts))
126
+ latex_environment('description', el, inner(el, opts))
134
127
  end
135
128
 
136
129
  def convert_li(el, opts)
137
- "\\item #{inner(el, opts)}\n"
130
+ "\\item #{inner(el, opts).sub(/\n+\Z/, '')}\n"
138
131
  end
139
132
 
140
133
  def convert_dt(el, opts)
@@ -170,7 +163,7 @@ module Kramdown
170
163
 
171
164
  def convert_table(el, opts)
172
165
  align = el.options[:alignment].map {|a| TABLE_ALIGNMENT_CHAR[a]}.join('|')
173
- "\\begin{tabular}{|#{align}|}\n\\hline\n#{inner(el, opts)}\\hline\n\\end{tabular}\n\n"
166
+ "\\begin{tabular}{|#{align}|}#{attribute_list(el)}\n\\hline\n#{inner(el, opts)}\\hline\n\\end{tabular}\n\n"
174
167
  end
175
168
 
176
169
  def convert_thead(el, opts)
@@ -194,6 +187,10 @@ module Kramdown
194
187
  end
195
188
  alias :convert_th :convert_td
196
189
 
190
+ def convert_comment(el, opts)
191
+ el.value.split(/\n/).map {|l| "% #{l}"}.join("\n") + "\n"
192
+ end
193
+
197
194
  def convert_br(el, opts)
198
195
  "\\newline\n"
199
196
  end
@@ -221,7 +218,7 @@ module Kramdown
221
218
  end
222
219
 
223
220
  def convert_codespan(el, opts)
224
- "{\\tt #{el.value ? escape(el.value) : inner(el, opts)}}"
221
+ "{\\tt #{escape(el.value)}}"
225
222
  end
226
223
 
227
224
  def convert_footnote(el, opts)
@@ -241,263 +238,258 @@ module Kramdown
241
238
  "\\textbf{#{inner(el, opts)}}"
242
239
  end
243
240
 
244
- # Inspired by Maruku: entity conversion table taken from htmltolatex
245
- # (http://sourceforge.net/projects/htmltolatex/), with some small adjustments as noted
246
- ENTITY_CONV_TABLE = <<'EOF'
247
- <char num='913' name='Alpha' convertTo='$A$' />
248
- <char num='914' name='Beta' convertTo='$B$' />
249
- <char num='915' name='Gamma' convertTo='$\Gamma$' />
250
- <char num='916' name='Delta' convertTo='$\Delta$' />
251
- <char num='917' name='Epsilon' convertTo='$E$' />
252
- <char num='918' name='Zeta' convertTo='$Z$' />
253
- <char num='919' name='Eta' convertTo='$H$' />
254
- <char num='920' name='Theta' convertTo='$\Theta$' />
255
- <char num='921' name='Iota' convertTo='$I$' />
256
- <char num='922' name='Kappa' convertTo='$K$' />
257
- <char num='923' name='Lambda' convertTo='$\Lambda$' />
258
- <char num='924' name='Mu' convertTo='$M$' />
259
- <char num='925' name='Nu' convertTo='$N$' />
260
- <char num='926' name='Xi' convertTo='$\Xi$' />
261
- <char num='927' name='Omicron' convertTo='$O$' />
262
- <char num='928' name='Pi' convertTo='$\Pi$' />
263
- <char num='929' name='Rho' convertTo='$P$' />
264
- <char num='931' name='Sigma' convertTo='$\Sigma$' />
265
- <char num='932' name='Tau' convertTo='$T$' />
266
- <char num='933' name='Upsilon' convertTo='$Y$' />
267
- <char num='934' name='Phi' convertTo='$\Phi$' />
268
- <char num='935' name='Chi' convertTo='$X$' />
269
- <char num='936' name='Psi' convertTo='$\Psi$' />
270
- <char num='937' name='Omega' convertTo='$\Omega$' />
271
- <char num='945' name='alpha' convertTo='$\alpha$' />
272
- <char num='946' name='beta' convertTo='$\beta$' />
273
- <char num='947' name='gamma' convertTo='$\gamma$' />
274
- <char num='948' name='delta' convertTo='$\delta$' />
275
- <char num='949' name='epsilon' convertTo='$\epsilon$' />
276
- <char num='950' name='zeta' convertTo='$\zeta$' />
277
- <char num='951' name='eta' convertTo='$\eta$' />
278
- <char num='952' name='theta' convertTo='$\theta$' />
279
- <char num='953' name='iota' convertTo='$\iota$' />
280
- <char num='954' name='kappa' convertTo='$\kappa$' />
281
- <char num='955' name='lambda' convertTo='$\lambda$' />
282
- <char num='956' name='mu' convertTo='$\mu$' />
283
- <char num='957' name='nu' convertTo='$\nu$' />
284
- <char num='958' name='xi' convertTo='$\xi$' />
285
- <char num='959' name='omicron' convertTo='$o$' />
286
- <char num='960' name='pi' convertTo='$\pi$' />
287
- <char num='961' name='rho' convertTo='$\rho$' />
288
- <char num='963' name='sigma' convertTo='$\sigma$' />
289
- <char num='964' name='tau' convertTo='$\tau$' />
290
- <char num='965' name='upsilon' convertTo='$\upsilon$' />
291
- <char num='966' name='phi' convertTo='$\phi$' />
292
- <char num='967' name='chi' convertTo='$\chi$' />
293
- <char num='968' name='psi' convertTo='$\psi$' />
294
- <char num='969' name='omega' convertTo='$\omega$' />
295
- <char num='962' name='sigmaf' convertTo='$\varsigma$' />
296
- <char num='977' name='thetasym' convertTo='$\vartheta$' />
297
- <char num='982' name='piv' convertTo='$\varpi$' />
298
- <char num='8230' name='hellip' convertTo='\ldots' />
299
- <char num='8242' name='prime' convertTo='$\prime$' />
300
- <char num='8254' name='oline' convertTo='-' />
301
- <char num='8260' name='frasl' convertTo='/' />
302
- <char num='8472' name='weierp' convertTo='$\wp$' />
303
- <char num='8465' name='image' convertTo='$\Im$' />
304
- <char num='8476' name='real' convertTo='$\Re$' />
305
- <char num='8501' name='alefsym' convertTo='$\aleph$' />
306
- <char num='8226' name='bull' convertTo='$\bullet$' />
307
- <char num='8482' name='trade' convertTo='$^{\rm TM}$' /> <!-- \texttrademark -->
308
- <char num='8592' name='larr' convertTo='$\leftarrow$' />
309
- <char num='8594' name='rarr' convertTo='$\rightarrow$' />
310
- <char num='8593' name='uarr' convertTo='$\uparrow$' />
311
- <char num='8595' name='darr' convertTo='$\downarrow$' />
312
- <char num='8596' name='harr' convertTo='$\leftrightarrow$' />
313
- <char num='8629' name='crarr' convertTo='$\hookleftarrow$' />
314
- <char num='8657' name='uArr' convertTo='$\Uparrow$' />
315
- <char num='8659' name='dArr' convertTo='$\Downarrow$' />
316
- <char num='8656' name='lArr' convertTo='$\Leftarrow$' />
317
- <char num='8658' name='rArr' convertTo='$\Rightarrow$' />
318
- <char num='8660' name='hArr' convertTo='$\Leftrightarrow$' />
319
- <char num='8704' name='forall' convertTo='$\forall$' />
320
- <char num='8706' name='part' convertTo='$\partial$' />
321
- <char num='8707' name='exist' convertTo='$\exists$' />
322
- <char num='8709' name='empty' convertTo='$\emptyset$' />
323
- <char num='8711' name='nabla' convertTo='$\nabla$' />
324
- <char num='8712' name='isin' convertTo='$\in$' />
325
- <char num='8715' name='ni' convertTo='$\ni$' />
326
- <char num='8713' name='notin' convertTo='$\notin$' />
327
- <char num='8721' name='sum' convertTo='$\sum$' />
328
- <char num='8719' name='prod' convertTo='$\prod$' />
329
- <char num='8722' name='minus' convertTo='$-$' />
330
- <char num='8727' name='lowast' convertTo='$\ast$' />
331
- <char num='8730' name='radic' convertTo='$\surd$' />
332
- <char num='8733' name='prop' convertTo='$\propto$' />
333
- <char num='8734' name='infin' convertTo='$\infty$' />
334
- <char num='8736' name='ang' convertTo='$\angle$' />
335
- <char num='8743' name='and' convertTo='$\wedge$' />
336
- <char num='8744' name='or' convertTo='$\vee$' />
337
- <char num='8745' name='cup' convertTo='$\cup$' />
338
- <char num='8746' name='cap' convertTo='$\cap$' />
339
- <char num='8747' name='int' convertTo='$\int$' />
340
- <char num='8756' name='there4' convertTo='$\therefore$' /> <!-- amssymb --> --CHANGED!!!
341
- <char num='8764' name='sim' convertTo='$\sim$' />
342
- <char num='8776' name='asymp' convertTo='$\approx$' />
343
- <char num='8773' name='cong' convertTo='$\cong$' />
344
- <char num='8800' name='ne' convertTo='$\neq$' />
345
- <char num='8801' name='equiv' convertTo='$\equiv$' />
346
- <char num='8804' name='le' convertTo='$\leq$' />
347
- <char num='8805' name='ge' convertTo='$\geq$' />
348
- <char num='8834' name='sub' convertTo='$\subset$' />
349
- <char num='8835' name='sup' convertTo='$\supset$' />
350
- <char num='8838' name='sube' convertTo='$\subseteq$' />
351
- <char num='8839' name='supe' convertTo='$\supseteq$' />
352
- <char num='8836' name='nsub' convertTo='$\nsubset$' /> <!-- amssymb --> --CHANGED!!!
353
- <char num='8853' name='oplus' convertTo='$\oplus$' />
354
- <char num='8855' name='otimes' convertTo='$\otimes$' />
355
- <char num='8869' name='perp' convertTo='$\perp$' />
356
- <char num='8901' name='sdot' convertTo='$\cdot$' />
357
- <char num='8968' name='rceil' convertTo='$\rceil$' />
358
- <char num='8969' name='lceil' convertTo='$\lceil$' />
359
- <char num='8970' name='lfloor' convertTo='$\lfloor$' />
360
- <char num='8971' name='rfloor' convertTo='$\rfloor$' />
361
- <char num='9001' name='rang' convertTo='$\rangle$' />
362
- <char num='9002' name='lang' convertTo='$\langle$' />
363
- <char num='9674' name='loz' convertTo='$\lozenge$' /> <!-- amssymb --> --CHANGED!!!
364
- <char num='9824' name='spades' convertTo='$\spadesuit$' />
365
- <char num='9827' name='clubs' convertTo='$\clubsuit$' />
366
- <char num='9829' name='hearts' convertTo='$\heartsuit$' />
367
- <char num='9830' name='diams' convertTo='$\diamondsuit$' />
368
- <char num='38' name='amp' convertTo='\@AMP' />
369
- <char num='34' name='quot' convertTo='@DOUBLEQUOT' />
370
- <char num='39' name='apos' convertTo='@QUOT' /> -- ADDED!!!
371
- <char num='169' name='copy' convertTo='\copyright' />
372
- <char num='60' name='lt' convertTo='\textless{}' /> -- CHANGED!!!
373
- <char num='62' name='gt' convertTo='\textgreater{}' /> -- CHANGED!!!
374
- <char num='338' name='OElig' convertTo='\OE' />
375
- <char num='339' name='oelig' convertTo='\oe' />
376
- <char num='352' name='Scaron' convertTo='\v{S}' />
377
- <char num='353' name='scaron' convertTo='\v{s}' />
378
- <char num='376' name='Yuml' convertTo='\"Y' />
379
- <char num='710' name='circ' convertTo='\textasciicircum' />
380
- <char num='732' name='tilde' convertTo='\textasciitilde' />
381
- <char num='8211' name='ndash' convertTo='--' />
382
- <char num='8212' name='mdash' convertTo='---' />
383
- <char num='8216' name='lsquo' convertTo='`' />
384
- <char num='8217' name='rsquo' convertTo='@QUOT' />
385
- <char num='8220' name='ldquo' convertTo='``' />
386
- <char num='8221' name='rdquo' convertTo='@QUOT@QUOT' />
387
- <char num='8224' name='dagger' convertTo='\dag' />
388
- <char num='8225' name='Dagger' convertTo='\ddag' />
389
- <char num='8240' name='permil' convertTo='\permil' /> <!-- wasysym package -->
390
- <char num='8364' name='euro' convertTo='\euro' /> <!-- eurosym package -->
391
- <char num='8249' name='lsaquo' convertTo='\guilsinglleft' />
392
- <char num='8250' name='rsaquo' convertTo='\guilsinglright' />
393
- <char num='160' name='nbsp' convertTo='\nolinebreak' />
394
- <char num='161' name='iexcl' convertTo='\textexclamdown' />
395
- <char num='163' name='pound' convertTo='\pounds' />
396
- <char num='164' name='curren' convertTo='\currency' /> <!-- wasysym package -->
397
- <char num='165' name='yen' convertTo='\textyen' /> <!-- textcomp -->
398
- <char num='166' name='brvbar' convertTo='\brokenvert' /> <!-- wasysym -->
399
- <char num='167' name='sect' convertTo='\S' />
400
- <char num='171' name='laquo' convertTo='\guillemotleft' />
401
- <char num='187' name='raquo' convertTo='\guillemotright' />
402
- <char num='174' name='reg' convertTo='\textregistered' />
403
- <char num='170' name='ordf' convertTo='\textordfeminine' />
404
- <char num='172' name='not' convertTo='$\neg$' />
405
- <char num='176' name='deg' convertTo='$\degree$' /> <!-- mathabx -->
406
- <char num='177' name='plusmn' convertTo='$\pm$' />
407
- <char num='180' name='acute' convertTo='@QUOT' />
408
- <char num='181' name='micro' convertTo='$\mu$' />
409
- <char num='182' name='para' convertTo='\P' />
410
- <char num='183' name='middot' convertTo='$\cdot$' />
411
- <char num='186' name='ordm' convertTo='\textordmasculine' />
412
- <char num='162' name='cent' convertTo='\cent' /> <!-- wasysym -->
413
- <char num='185' name='sup1' convertTo='$^1$' />
414
- <char num='178' name='sup2' convertTo='$^2$' />
415
- <char num='179' name='sup3' convertTo='$^3$' />
416
- <char num='189' name='frac12' convertTo='$\frac{1}{2}$' />
417
- <char num='188' name='frac14' convertTo='$\frac{1}{4}$' />
418
- <char num='190' name='frac34' convertTo='$\frac{3}{4}' />
419
- <char num='192' name='Agrave' convertTo='\`A' />
420
- <char num='193' name='Aacute' convertTo='\@QUOTA' />
421
- <char num='194' name='Acirc' convertTo='\^A' />
422
- <char num='195' name='Atilde' convertTo='\~A' />
423
- <char num='196' name='Auml' convertTo='\@DOUBLEQUOTA' />
424
- <char num='197' name='Aring' convertTo='\AA' />
425
- <char num='198' name='AElig' convertTo='\AE' />
426
- <char num='199' name='Ccedil' convertTo='\cC' />
427
- <char num='200' name='Egrave' convertTo='\`E' />
428
- <char num='201' name='Eacute' convertTo='\@QUOTE' />
429
- <char num='202' name='Ecirc' convertTo='\^E' />
430
- <char num='203' name='Euml' convertTo='\@DOUBLEQUOTE' />
431
- <char num='204' name='Igrave' convertTo='\`I' />
432
- <char num='205' name='Iacute' convertTo='\@QUOTI' />
433
- <char num='206' name='Icirc' convertTo='\^I' />
434
- <char num='207' name='Iuml' convertTo='\"I' />
435
- <char num='208' name='ETH' convertTo='$\eth$' /> <!-- amssymb --> --CHANGED!!!
436
- <char num='209' name='Ntilde' convertTo='\~N' />
437
- <char num='210' name='Ograve' convertTo='\`O' />
438
- <char num='211' name='Oacute' convertTo='\@QUOTO' />
439
- <char num='212' name='Ocirc' convertTo='\^O' />
440
- <char num='213' name='Otilde' convertTo='\~O' />
441
- <char num='214' name='Ouml' convertTo='\@DOUBLEQUOTO' />
442
- <char num='215' name='times' convertTo='$\times$' />
443
- <char num='216' name='Oslash' convertTo='\O' />
444
- <char num='217' name='Ugrave' convertTo='\`U' />
445
- <char num='218' name='Uacute' convertTo='\@QUOTU' />
446
- <char num='219' name='Ucirc' convertTo='\^U' />
447
- <char num='220' name='Uuml' convertTo='\@DOUBLEQUOTU' />
448
- <char num='221' name='Yacute' convertTo='\@QUOTY' />
449
- <char num='222' name='THORN' convertTo='\Thorn' /> <!-- wasysym -->
450
- <char num='223' name='szlig' convertTo='\ss' />
451
- <char num='224' name='agrave' convertTo='\`a' />
452
- <char num='225' name='aacute' convertTo='\@QUOTa' />
453
- <char num='226' name='acirc' convertTo='\^a' />
454
- <char num='227' name='atilde' convertTo='\~a' />
455
- <char num='228' name='auml' convertTo='\@DOUBLEQUOTa' />
456
- <char num='229' name='aring' convertTo='\aa' />
457
- <char num='230' name='aelig' convertTo='\ae' />
458
- <char num='231' name='ccedil' convertTo='\cc' />
459
- <char num='232' name='egrave' convertTo='\`e' />
460
- <char num='233' name='eacute' convertTo='\@QUOTe' />
461
- <char num='234' name='ecirc' convertTo='\^e' />
462
- <char num='235' name='euml' convertTo='\@DOUBLEQUOTe' />
463
- <char num='236' name='igrave' convertTo='\`i' />
464
- <char num='237' name='iacute' convertTo='\@QUOTi' />
465
- <char num='238' name='icirc' convertTo='\^i' />
466
- <char num='239' name='iuml' convertTo='\@DOUBLEQUOTi' />
467
- <char num='240' name='eth' convertTo='$\eth$' /> <!-- -->
468
- <char num='241' name='ntilde' convertTo='\~n' />
469
- <char num='242' name='ograve' convertTo='\`o' />
470
- <char num='243' name='oacute' convertTo='\@QUOTo' />
471
- <char num='244' name='ocirc' convertTo='\^o' />
472
- <char num='245' name='otilde' convertTo='\~o' />
473
- <char num='246' name='ouml' convertTo='\@DOUBLEQUOTo' />
474
- <char num='247' name='divide' convertTo='$\divide$' />
475
- <char num='248' name='oslash' convertTo='\o' />
476
- <char num='249' name='ugrave' convertTo='\`u' />
477
- <char num='250' name='uacute' convertTo='\@QUOTu' />
478
- <char num='251' name='ucirc' convertTo='\^u' />
479
- <char num='252' name='uuml' convertTo='\@DOUBLEQUOTu' />
480
- <char num='253' name='yacute' convertTo='\@QUOTy' />
481
- <char num='254' name='thorn' convertTo='\thorn' /> <!-- wasysym -->
482
- <char num='255' name='yuml' convertTo='\@DOUBLEQUOTy' />
483
- EOF
484
- ENTITY_MAP = {}
485
- ENTITY_CONV_TABLE.split(/\n/).each do |line|
486
- num = line.scan(/num='(\d+)'/).first.first.to_i
487
- name = line.scan(/name='(.*?)'/).first.first
488
- latex = line.scan(/convertTo='(.*?)'/).first.first.gsub(/@AMP/, '&').
489
- gsub(/@QUOT/, '\'').gsub(/@DOUBLEQUOT/, '"').gsub(/@LT/, '<').gsub(/@GT/, '>') + '{}'
490
- package = line.scan(/<!--\s*(\w+)/).first
491
- package = package.first unless package.nil?
492
- ENTITY_MAP[num] = [latex, package]
493
- ENTITY_MAP[name] = [latex, package]
494
- end
241
+ # Inspired by Maruku: entity conversion table based on the one from htmltolatex
242
+ # (http://sourceforge.net/projects/htmltolatex/), with some small adjustments/additions
243
+ ENTITY_CONV_TABLE = {
244
+ 913 => ['$A$'],
245
+ 914 => ['$B$'],
246
+ 915 => ['$\Gamma$'],
247
+ 916 => ['$\Delta$'],
248
+ 917 => ['$E$'],
249
+ 918 => ['$Z$'],
250
+ 919 => ['$H$'],
251
+ 920 => ['$\Theta$'],
252
+ 921 => ['$I$'],
253
+ 922 => ['$K$'],
254
+ 923 => ['$\Lambda$'],
255
+ 924 => ['$M$'],
256
+ 925 => ['$N$'],
257
+ 926 => ['$\Xi$'],
258
+ 927 => ['$O$'],
259
+ 928 => ['$\Pi$'],
260
+ 929 => ['$P$'],
261
+ 931 => ['$\Sigma$'],
262
+ 932 => ['$T$'],
263
+ 933 => ['$Y$'],
264
+ 934 => ['$\Phi$'],
265
+ 935 => ['$X$'],
266
+ 936 => ['$\Psi$'],
267
+ 937 => ['$\Omega$'],
268
+ 945 => ['$\alpha$'],
269
+ 946 => ['$\beta$'],
270
+ 947 => ['$\gamma$'],
271
+ 948 => ['$\delta$'],
272
+ 949 => ['$\epsilon$'],
273
+ 950 => ['$\zeta$'],
274
+ 951 => ['$\eta$'],
275
+ 952 => ['$\theta$'],
276
+ 953 => ['$\iota$'],
277
+ 954 => ['$\kappa$'],
278
+ 955 => ['$\lambda$'],
279
+ 956 => ['$\mu$'],
280
+ 957 => ['$\nu$'],
281
+ 958 => ['$\xi$'],
282
+ 959 => ['$o$'],
283
+ 960 => ['$\pi$'],
284
+ 961 => ['$\rho$'],
285
+ 963 => ['$\sigma$'],
286
+ 964 => ['$\tau$'],
287
+ 965 => ['$\upsilon$'],
288
+ 966 => ['$\phi$'],
289
+ 967 => ['$\chi$'],
290
+ 968 => ['$\psi$'],
291
+ 969 => ['$\omega$'],
292
+ 962 => ['$\varsigma$'],
293
+ 977 => ['$\vartheta$'],
294
+ 982 => ['$\varpi$'],
295
+ 8230 => ['\ldots'],
296
+ 8242 => ['$\prime$'],
297
+ 8254 => ['-'],
298
+ 8260 => ['/'],
299
+ 8472 => ['$\wp$'],
300
+ 8465 => ['$\Im$'],
301
+ 8476 => ['$\Re$'],
302
+ 8501 => ['$\aleph$'],
303
+ 8226 => ['$\bullet$'],
304
+ 8482 => ['$^{\rm TM}$'],
305
+ 8592 => ['$\leftarrow$'],
306
+ 8594 => ['$\rightarrow$'],
307
+ 8593 => ['$\uparrow$'],
308
+ 8595 => ['$\downarrow$'],
309
+ 8596 => ['$\leftrightarrow$'],
310
+ 8629 => ['$\hookleftarrow$'],
311
+ 8657 => ['$\Uparrow$'],
312
+ 8659 => ['$\Downarrow$'],
313
+ 8656 => ['$\Leftarrow$'],
314
+ 8658 => ['$\Rightarrow$'],
315
+ 8660 => ['$\Leftrightarrow$'],
316
+ 8704 => ['$\forall$'],
317
+ 8706 => ['$\partial$'],
318
+ 8707 => ['$\exists$'],
319
+ 8709 => ['$\emptyset$'],
320
+ 8711 => ['$\nabla$'],
321
+ 8712 => ['$\in$'],
322
+ 8715 => ['$\ni$'],
323
+ 8713 => ['$\notin$'],
324
+ 8721 => ['$\sum$'],
325
+ 8719 => ['$\prod$'],
326
+ 8722 => ['$-$'],
327
+ 8727 => ['$\ast$'],
328
+ 8730 => ['$\surd$'],
329
+ 8733 => ['$\propto$'],
330
+ 8734 => ['$\infty$'],
331
+ 8736 => ['$\angle$'],
332
+ 8743 => ['$\wedge$'],
333
+ 8744 => ['$\vee$'],
334
+ 8745 => ['$\cup$'],
335
+ 8746 => ['$\cap$'],
336
+ 8747 => ['$\int$'],
337
+ 8756 => ['$\therefore$', 'amssymb'],
338
+ 8764 => ['$\sim$'],
339
+ 8776 => ['$\approx$'],
340
+ 8773 => ['$\cong$'],
341
+ 8800 => ['$\neq$'],
342
+ 8801 => ['$\equiv$'],
343
+ 8804 => ['$\leq$'],
344
+ 8805 => ['$\geq$'],
345
+ 8834 => ['$\subset$'],
346
+ 8835 => ['$\supset$'],
347
+ 8838 => ['$\subseteq$'],
348
+ 8839 => ['$\supseteq$'],
349
+ 8836 => ['$\nsubset$', 'amssymb'],
350
+ 8853 => ['$\oplus$'],
351
+ 8855 => ['$\otimes$'],
352
+ 8869 => ['$\perp$'],
353
+ 8901 => ['$\cdot$'],
354
+ 8968 => ['$\rceil$'],
355
+ 8969 => ['$\lceil$'],
356
+ 8970 => ['$\lfloor$'],
357
+ 8971 => ['$\rfloor$'],
358
+ 9001 => ['$\rangle$'],
359
+ 9002 => ['$\langle$'],
360
+ 9674 => ['$\lozenge$', 'amssymb'],
361
+ 9824 => ['$\spadesuit$'],
362
+ 9827 => ['$\clubsuit$'],
363
+ 9829 => ['$\heartsuit$'],
364
+ 9830 => ['$\diamondsuit$'],
365
+ 38 => ['\&'],
366
+ 34 => ['"'],
367
+ 39 => ['\''],
368
+ 169 => ['\copyright'],
369
+ 60 => ['\textless{}'],
370
+ 62 => ['\textgreater{}'],
371
+ 338 => ['\OE'],
372
+ 339 => ['\oe'],
373
+ 352 => ['\v{S}'],
374
+ 353 => ['\v{s}'],
375
+ 376 => ['\"Y'],
376
+ 710 => ['\textasciicircum'],
377
+ 732 => ['\textasciitilde'],
378
+ 8211 => ['--'],
379
+ 8212 => ['---'],
380
+ 8216 => ['`'],
381
+ 8217 => ['\''],
382
+ 8220 => ['``'],
383
+ 8221 => ['\'\''],
384
+ 8224 => ['\dag'],
385
+ 8225 => ['\ddag'],
386
+ 8240 => ['\permil', 'wasysym'],
387
+ 8364 => ['\euro', 'eurosym'],
388
+ 8249 => ['\guilsinglleft'],
389
+ 8250 => ['\guilsinglright'],
390
+ 8218 => ['\quotesinglbase', 'mathcomp'],
391
+ 8222 => ['\quotedblbase', 'mathcomp'],
392
+ 402 => ['\textflorin', 'mathcomp'],
393
+ 381 => ['\v{Z}'],
394
+ 382 => ['\v{z}'],
395
+ 160 => ['\nolinebreak'],
396
+ 161 => ['\textexclamdown'],
397
+ 163 => ['\pounds'],
398
+ 164 => ['\currency', 'wasysym'],
399
+ 165 => ['\textyen', 'textcomp'],
400
+ 166 => ['\brokenvert', 'wasysym'],
401
+ 167 => ['\S'],
402
+ 171 => ['\guillemotleft'],
403
+ 187 => ['\guillemotright'],
404
+ 174 => ['\textregistered'],
405
+ 170 => ['\textordfeminine'],
406
+ 172 => ['$\neg$'],
407
+ 176 => ['$\degree$', 'mathabx'],
408
+ 177 => ['$\pm$'],
409
+ 180 => ['\''],
410
+ 181 => ['$\mu$'],
411
+ 182 => ['\P'],
412
+ 183 => ['$\cdot$'],
413
+ 186 => ['\textordmasculine'],
414
+ 162 => ['\cent', 'wasysym'],
415
+ 185 => ['$^1$'],
416
+ 178 => ['$^2$'],
417
+ 179 => ['$^3$'],
418
+ 189 => ['$\frac{1}{2}$'],
419
+ 188 => ['$\frac{1}{4}$'],
420
+ 190 => ['$\frac{3}{4}'],
421
+ 192 => ['\`A'],
422
+ 193 => ['\\\'A'],
423
+ 194 => ['\^A'],
424
+ 195 => ['\~A'],
425
+ 196 => ['\"A'],
426
+ 197 => ['\AA'],
427
+ 198 => ['\AE'],
428
+ 199 => ['\cC'],
429
+ 200 => ['\`E'],
430
+ 201 => ['\\\'E'],
431
+ 202 => ['\^E'],
432
+ 203 => ['\"E'],
433
+ 204 => ['\`I'],
434
+ 205 => ['\\\'I'],
435
+ 206 => ['\^I'],
436
+ 207 => ['\"I'],
437
+ 208 => ['$\eth$', 'amssymb'],
438
+ 209 => ['\~N'],
439
+ 210 => ['\`O'],
440
+ 211 => ['\\\'O'],
441
+ 212 => ['\^O'],
442
+ 213 => ['\~O'],
443
+ 214 => ['\"O'],
444
+ 215 => ['$\times$'],
445
+ 216 => ['\O'],
446
+ 217 => ['\`U'],
447
+ 218 => ['\\\'U'],
448
+ 219 => ['\^U'],
449
+ 220 => ['\"U'],
450
+ 221 => ['\\\'Y'],
451
+ 222 => ['\Thorn', 'wasysym'],
452
+ 223 => ['\ss'],
453
+ 224 => ['\`a'],
454
+ 225 => ['\\\'a'],
455
+ 226 => ['\^a'],
456
+ 227 => ['\~a'],
457
+ 228 => ['\"a'],
458
+ 229 => ['\aa'],
459
+ 230 => ['\ae'],
460
+ 231 => ['\cc'],
461
+ 232 => ['\`e'],
462
+ 233 => ['\\\'e'],
463
+ 234 => ['\^e'],
464
+ 235 => ['\"e'],
465
+ 236 => ['\`i'],
466
+ 237 => ['\\\'i'],
467
+ 238 => ['\^i'],
468
+ 239 => ['\"i'],
469
+ 240 => ['$\eth$'],
470
+ 241 => ['\~n'],
471
+ 242 => ['\`o'],
472
+ 243 => ['\\\'o'],
473
+ 244 => ['\^o'],
474
+ 245 => ['\~o'],
475
+ 246 => ['\"o'],
476
+ 247 => ['$\divide$'],
477
+ 248 => ['\o'],
478
+ 249 => ['\`u'],
479
+ 250 => ['\\\'u'],
480
+ 251 => ['\^u'],
481
+ 252 => ['\"u'],
482
+ 253 => ['\\\'y'],
483
+ 254 => ['\thorn', 'wasysym'],
484
+ 255 => ['\"y'],
485
+ }
486
+ ENTITY_CONV_TABLE.each {|k,v| ENTITY_CONV_TABLE[k] = v.unshift(v.shift + '{}')}
495
487
 
496
488
  def convert_entity(el, opts)
497
- result = ENTITY_MAP[el.value]
498
- if result
499
- @doc.conversion_infos[:packages] << result[1] if result[1]
500
- result[0]
489
+ text, package = ENTITY_CONV_TABLE[el.value.code_point]
490
+ if text
491
+ @doc.conversion_infos[:packages] << package if package
492
+ text
501
493
  else
502
494
  @doc.warnings << "Couldn't find entity in substitution table!"
503
495
  ''
@@ -524,7 +516,7 @@ EOF
524
516
  if el.value =~ /\A\s*\\begin\{/
525
517
  el.value
526
518
  else
527
- latex_environment('displaymath', el.value)
519
+ latex_environment('displaymath', el, el.value)
528
520
  end
529
521
  else
530
522
  "$#{el.value}$"
@@ -549,6 +541,12 @@ EOF
549
541
  str.gsub(ESCAPE_RE) {|m| ESCAPE_MAP[m]}
550
542
  end
551
543
 
544
+ def attribute_list(el)
545
+ attrs = (el.options[:attr] || {}).map {|k,v| v.nil? ? '' : " #{k}=\"#{v.to_s}\""}.compact.sort.join('')
546
+ attrs = " % #{attrs}" if !attrs.empty?
547
+ attrs
548
+ end
549
+
552
550
  end
553
551
 
554
552
  end