class Kramdown::Converter::Html

Converts a Kramdown::Document to HTML.

You can customize the HTML converter by sub-classing it and overriding the convert_NAME methods. Each such method takes the following parameters:

el

The element of type NAME to be converted.

indent

A number representing the current amount of spaces for indent (only used for block-level elements).

The return value of such a method has to be a string containing the element el formatted as HTML element.

Constants

DISPATCHER

The mapping of element type to conversion method.

Attributes

indent[RW]

The amount of indentation used when nesting HTML tags.

Public Class Methods

new(root, options) click to toggle source

Initialize the HTML converter with the given Kramdown document doc.

Calls superclass method Kramdown::Converter::Base::new
   # File lib/kramdown/converter/html.rb
38 def initialize(root, options)
39   super
40   @footnote_counter = @footnote_start = @options[:footnote_nr]
41   @footnotes = []
42   @footnotes_by_name = {}
43   @footnote_location = nil
44   @toc = []
45   @toc_code = nil
46   @indent = 2
47   @stack = []
48 end

Public Instance Methods

add_syntax_highlighter_to_class_attr(attr, lang = nil) click to toggle source

Add the syntax highlighter name to the 'class' attribute of the given attribute hash. And overwrites or add a “language-LANG” part using the lang parameter if lang is not nil.

    # File lib/kramdown/converter/html.rb
376 def add_syntax_highlighter_to_class_attr(attr, lang = nil)
377   (attr['class'] = (attr['class'] || '') + " highlighter-#{@options[:syntax_highlighter]}").lstrip!
378   attr['class'].sub!(/\blanguage-\S+|(^)/) { "language-#{lang}#{$1 ? ' ' : ''}" } if lang
379 end
convert(el, indent = -@indent) click to toggle source

Dispatch the conversion of the element el to a convert_TYPE method using the type of the element.

   # File lib/kramdown/converter/html.rb
55 def convert(el, indent = -@indent)
56   send(DISPATCHER[el.type], el, indent)
57 end
convert_a(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
244 def convert_a(el, indent)
245   format_as_span_html(el.type, el.attr, inner(el, indent))
246 end
convert_abbreviation(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
332 def convert_abbreviation(el, indent)
333   title = @root.options[:abbrev_defs][el.value]
334   attr = @root.options[:abbrev_attr][el.value].dup
335   attr['title'] = title unless title.empty?
336   format_as_span_html("abbr", attr, el.value)
337 end
convert_blank(el, indent) click to toggle source
   # File lib/kramdown/converter/html.rb
75 def convert_blank(el, indent)
76   "\n"
77 end
convert_blockquote(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
120 def convert_blockquote(el, indent)
121   format_as_indented_block_html(el.type, el.attr, inner(el, indent), indent)
122 end
convert_br(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
240 def convert_br(el, indent)
241   "<br />"
242 end
convert_codeblock(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
 91 def convert_codeblock(el, indent)
 92   attr = el.attr.dup
 93   lang = extract_code_language!(attr)
 94   hl_opts = {}
 95   highlighted_code = highlight_code(el.value, el.options[:lang] || lang, :block, hl_opts)
 96 
 97   if highlighted_code
 98     add_syntax_highlighter_to_class_attr(attr, lang || hl_opts[:default_lang])
 99     "#{' '*indent}<div#{html_attributes(attr)}>#{highlighted_code}#{' '*indent}</div>\n"
100   else
101     result = escape_html(el.value)
102     result.chomp!
103     if el.attr['class'].to_s =~ /\bshow-whitespaces\b/
104       result.gsub!(/(?:(^[ \t]+)|([ \t]+$)|([ \t]+))/) do |m|
105         suffix = ($1 ? '-l' : ($2 ? '-r' : ''))
106         m.scan(/./).map do |c|
107           case c
108           when "\t" then "<span class=\"ws-tab#{suffix}\">\t</span>"
109           when " " then "<span class=\"ws-space#{suffix}\">&#8901;</span>"
110           end
111         end.join('')
112       end
113     end
114     code_attr = {}
115     code_attr['class'] = "language-#{lang}" if lang
116     "#{' '*indent}<pre#{html_attributes(attr)}><code#{html_attributes(code_attr)}>#{result}\n</code></pre>\n"
117   end
118 end
convert_codespan(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
252 def convert_codespan(el, indent)
253   attr = el.attr.dup
254   lang = extract_code_language(attr)
255   hl_opts = {}
256   result = highlight_code(el.value, lang, :span, hl_opts)
257   if result
258     add_syntax_highlighter_to_class_attr(attr, hl_opts[:default_lang])
259   else
260     result = escape_html(el.value)
261   end
262 
263   format_as_span_html('code', attr, result)
264 end
convert_comment(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
232 def convert_comment(el, indent)
233   if el.options[:category] == :block
234     "#{' '*indent}<!-- #{el.value} -->\n"
235   else
236     "<!-- #{el.value} -->"
237   end
238 end
convert_dd(el, indent)
Alias for: convert_li
convert_dl(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
150 def convert_dl(el, indent)
151   format_as_indented_block_html(el.type, el.attr, inner(el, indent), indent)
152 end
convert_dt(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
166 def convert_dt(el, indent)
167   attr = el.attr.dup
168   @stack.last.options[:ial][:refs].each do |ref|
169     if ref =~ /\Aauto_ids(?:-([\w-]+))?/
170       attr['id'] = ($1 ? $1 : '') << basic_generate_id(el.options[:raw_text])
171       break
172     end
173   end if !attr['id'] && @stack.last.options[:ial] && @stack.last.options[:ial][:refs]
174   format_as_block_html(el.type, attr, inner(el, indent), indent)
175 end
convert_em(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
288 def convert_em(el, indent)
289   format_as_span_html(el.type, el.attr, inner(el, indent))
290 end
Also aliased as: convert_strong
convert_entity(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
293 def convert_entity(el, indent)
294   entity_to_str(el.value, el.options[:original])
295 end
convert_footnote(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
266 def convert_footnote(el, indent)
267   repeat = ''
268   if (footnote = @footnotes_by_name[el.options[:name]])
269     number = footnote[2]
270     repeat = ":#{footnote[3] += 1}"
271   else
272     number = @footnote_counter
273     @footnote_counter += 1
274     @footnotes << [el.options[:name], el.value, number, 0]
275     @footnotes_by_name[el.options[:name]] = @footnotes.last
276   end
277   "<sup id=\"fnref:#{el.options[:name]}#{repeat}\"><a href=\"#fn:#{el.options[:name]}\" class=\"footnote\">#{number}</a></sup>"
278 end
convert_header(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
124 def convert_header(el, indent)
125   attr = el.attr.dup
126   if @options[:auto_ids] && !attr['id']
127     attr['id'] = generate_id(el.options[:raw_text])
128   end
129   @toc << [el.options[:level], attr['id'], el.children] if attr['id'] && in_toc?(el)
130   level = output_header_level(el.options[:level])
131   format_as_block_html("h#{level}", attr, inner(el, indent), indent)
132 end
convert_hr(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
134 def convert_hr(el, indent)
135   "#{' '*indent}<hr#{html_attributes(el.attr)} />\n"
136 end
convert_html_element(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
177 def convert_html_element(el, indent)
178   res = inner(el, indent)
179   if el.options[:category] == :span
180     "<#{el.value}#{html_attributes(el.attr)}" << (res.empty? && HTML_ELEMENTS_WITHOUT_BODY.include?(el.value) ? " />" : ">#{res}</#{el.value}>")
181   else
182     output = ''
183     output << ' '*indent if @stack.last.type != :html_element || @stack.last.options[:content_model] != :raw
184     output << "<#{el.value}#{html_attributes(el.attr)}"
185     if el.options[:is_closed] && el.options[:content_model] == :raw
186       output << " />"
187     elsif !res.empty? && el.options[:content_model] != :block
188       output << ">#{res}</#{el.value}>"
189     elsif !res.empty?
190       output << ">\n#{res.chomp}\n"  << ' '*indent << "</#{el.value}>"
191     elsif HTML_ELEMENTS_WITHOUT_BODY.include?(el.value)
192       output << " />"
193     else
194       output << "></#{el.value}>"
195     end
196     output << "\n" if @stack.last.type != :html_element || @stack.last.options[:content_model] != :raw
197     output
198   end
199 end
convert_img(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
248 def convert_img(el, indent)
249   "<img#{html_attributes(el.attr)} />"
250 end
convert_li(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
154 def convert_li(el, indent)
155   output = ' '*indent << "<#{el.type}" << html_attributes(el.attr) << ">"
156   res = inner(el, indent)
157   if el.children.empty? || (el.children.first.type == :p && el.children.first.options[:transparent])
158     output << res << (res =~ /\n\Z/ ? ' '*indent : '')
159   else
160     output << "\n" << res << ' '*indent
161   end
162   output << "</#{el.type}>\n"
163 end
Also aliased as: convert_dd
convert_math(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
318 def convert_math(el, indent)
319   if (result = format_math(el, :indent => indent))
320     result
321   else
322     attr = el.attr.dup
323     (attr['class'] = (attr['class'] || '') << " kdmath").lstrip!
324     if el.options[:category] == :block
325       format_as_block_html('div', attr, "$$\n#{el.value}\n$$", indent)
326     else
327       format_as_span_html('span', attr, "$#{el.value}$")
328     end
329   end
330 end
convert_ol(el, indent)
Alias for: convert_ul
convert_p(el, indent) click to toggle source
   # File lib/kramdown/converter/html.rb
83 def convert_p(el, indent)
84   if el.options[:transparent]
85     inner(el, indent)
86   else
87     format_as_block_html(el.type, el.attr, inner(el, indent), indent)
88   end
89 end
convert_raw(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
280 def convert_raw(el, indent)
281   if !el.options[:type] || el.options[:type].empty? || el.options[:type].include?('html')
282     el.value + (el.options[:category] == :block ? "\n" : '')
283   else
284     ''
285   end
286 end
convert_root(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
339 def convert_root(el, indent)
340   result = inner(el, indent)
341   if @footnote_location
342     result.sub!(/#{@footnote_location}/, footnote_content.gsub(/\\/, "\\\\\\\\"))
343   else
344     result << footnote_content
345   end
346   if @toc_code
347     toc_tree = generate_toc_tree(@toc, @toc_code[0], @toc_code[1] || {})
348     text = if toc_tree.children.size > 0
349              convert(toc_tree, 0)
350            else
351              ''
352            end
353     result.sub!(/#{@toc_code.last}/, text.gsub(/\\/, "\\\\\\\\"))
354   end
355   result
356 end
convert_smart_quote(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
314 def convert_smart_quote(el, indent)
315   entity_to_str(smart_quote_entity(el))
316 end
convert_strong(el, indent)
Alias for: convert_em
convert_table(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
210 def convert_table(el, indent)
211   format_as_indented_block_html(el.type, el.attr, inner(el, indent), indent)
212 end
convert_tbody(el, indent)
Alias for: convert_table
convert_td(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
220 def convert_td(el, indent)
221   res = inner(el, indent)
222   type = (@stack[-2].type == :thead ? :th : :td)
223   attr = el.attr
224   alignment = @stack[-3].options[:alignment][@stack.last.children.index(el)]
225   if alignment != :default
226     attr = el.attr.dup
227     attr['style'] = (attr.has_key?('style') ? "#{attr['style']}; ": '') << "text-align: #{alignment}"
228   end
229   format_as_block_html(type, attr, res.empty? ? entity_to_str(ENTITY_NBSP) : res, indent)
230 end
convert_text(el, indent) click to toggle source
   # File lib/kramdown/converter/html.rb
79 def convert_text(el, indent)
80   escape_html(el.value, :text)
81 end
convert_tfoot(el, indent)
Alias for: convert_table
convert_thead(el, indent)
Alias for: convert_table
convert_tr(el, indent)
Alias for: convert_table
convert_typographic_sym(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
306 def convert_typographic_sym(el, indent)
307   if (result = @options[:typographic_symbols][el.value])
308     escape_html(result, :text)
309   else
310     TYPOGRAPHIC_SYMS[el.value].map {|e| entity_to_str(e)}.join('')
311   end
312 end
convert_ul(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
138 def convert_ul(el, indent)
139   if !@toc_code && (el.options[:ial][:refs].include?('toc') rescue nil)
140     @toc_code = [el.type, el.attr, (0..128).to_a.map{|a| rand(36).to_s(36)}.join]
141     @toc_code.last
142   elsif !@footnote_location && el.options[:ial] && (el.options[:ial][:refs] || []).include?('footnotes')
143     @footnote_location = (0..128).to_a.map{|a| rand(36).to_s(36)}.join
144   else
145     format_as_indented_block_html(el.type, el.attr, inner(el, indent), indent)
146   end
147 end
Also aliased as: convert_ol
convert_xml_comment(el, indent) click to toggle source
    # File lib/kramdown/converter/html.rb
201 def convert_xml_comment(el, indent)
202   if el.options[:category] == :block && (@stack.last.type != :html_element || @stack.last.options[:content_model] != :raw)
203     ' '*indent << el.value << "\n"
204   else
205     el.value
206   end
207 end
Also aliased as: convert_xml_pi
convert_xml_pi(el, indent)
Alias for: convert_xml_comment
fix_for_toc_entry(elements) click to toggle source

Fixes the elements for use in a TOC entry.

    # File lib/kramdown/converter/html.rb
420 def fix_for_toc_entry(elements)
421   remove_footnotes(elements)
422   unwrap_links(elements)
423   elements
424 end
footnote_content() click to toggle source

Return a HTML ordered list with the footnote content for the used footnotes.

    # File lib/kramdown/converter/html.rb
455 def footnote_content
456   ol = Element.new(:ol)
457   ol.attr['start'] = @footnote_start if @footnote_start != 1
458   i = 0
459   backlink_text = escape_html(@options[:footnote_backlink], :text)
460   while i < @footnotes.length
461     name, data, _, repeat = *@footnotes[i]
462     li = Element.new(:li, nil, {'id' => "fn:#{name}"})
463     li.children = Marshal.load(Marshal.dump(data.children))
464 
465     para = nil
466     if li.children.last.type == :p || @options[:footnote_backlink_inline]
467       parent = li
468       while !parent.children.empty? && ![:p, :header].include?(parent.children.last.type)
469         parent = parent.children.last
470       end
471       para = parent.children.last
472       insert_space = true
473     end
474 
475     unless para
476       li.children << (para = Element.new(:p))
477       insert_space = false
478     end
479 
480     unless @options[:footnote_backlink].empty?
481       nbsp = entity_to_str(ENTITY_NBSP)
482       para.children << Element.new(:raw, FOOTNOTE_BACKLINK_FMT % [insert_space ? nbsp : '', name, backlink_text])
483       (1..repeat).each do |index|
484         para.children << Element.new(:raw, FOOTNOTE_BACKLINK_FMT % [nbsp, "#{name}:#{index}", "#{backlink_text}<sup>#{index+1}</sup>"])
485       end
486     end
487 
488     ol.children << Element.new(:raw, convert(li, 4))
489     i += 1
490   end
491   (ol.children.empty? ? '' : format_as_indented_block_html('div', {:class => "footnotes"}, convert(ol, 2), 0))
492 end
format_as_block_html(name, attr, body, indent) click to toggle source

Format the given element as block HTML.

    # File lib/kramdown/converter/html.rb
364 def format_as_block_html(name, attr, body, indent)
365   "#{' '*indent}<#{name}#{html_attributes(attr)}>#{body}</#{name}>\n"
366 end
format_as_indented_block_html(name, attr, body, indent) click to toggle source

Format the given element as block HTML with a newline after the start tag and indentation before the end tag.

    # File lib/kramdown/converter/html.rb
370 def format_as_indented_block_html(name, attr, body, indent)
371   "#{' '*indent}<#{name}#{html_attributes(attr)}>\n#{body}#{' '*indent}</#{name}>\n"
372 end
format_as_span_html(name, attr, body) click to toggle source

Format the given element as span HTML.

    # File lib/kramdown/converter/html.rb
359 def format_as_span_html(name, attr, body)
360   "<#{name}#{html_attributes(attr)}>#{body}</#{name}>"
361 end
generate_toc_tree(toc, type, attr) click to toggle source

Generate and return an element tree for the table of contents.

    # File lib/kramdown/converter/html.rb
382 def generate_toc_tree(toc, type, attr)
383   sections = Element.new(type, nil, attr)
384   sections.attr['id'] ||= 'markdown-toc'
385   stack = []
386   toc.each do |level, id, children|
387     li = Element.new(:li, nil, nil, {:level => level})
388     li.children << Element.new(:p, nil, nil, {:transparent => true})
389     a = Element.new(:a, nil)
390     a.attr['href'] = "##{id}"
391     a.attr['id'] = "#{sections.attr['id']}-#{id}"
392     a.children.concat(fix_for_toc_entry(Marshal.load(Marshal.dump(children))))
393     li.children.last.children << a
394     li.children << Element.new(type)
395 
396     success = false
397     while !success
398       if stack.empty?
399         sections.children << li
400         stack << li
401         success = true
402       elsif stack.last.options[:level] < li.options[:level]
403         stack.last.children.last.children << li
404         stack << li
405         success = true
406       else
407         item = stack.pop
408         item.children.pop unless item.children.last.children.size > 0
409       end
410     end
411   end
412   while !stack.empty?
413     item = stack.pop
414     item.children.pop unless item.children.last.children.size > 0
415   end
416   sections
417 end
inner(el, indent) click to toggle source

Return the converted content of the children of el as a string. The parameter indent has to be the amount of indentation used for the element el.

Pushes el onto the @stack before converting the child elements and pops it from the stack afterwards.

   # File lib/kramdown/converter/html.rb
64 def inner(el, indent)
65   result = ''
66   indent += @indent
67   @stack.push(el)
68   el.children.each do |inner_el|
69     result << send(DISPATCHER[inner_el.type], inner_el, indent)
70   end
71   @stack.pop
72   result
73 end
obfuscate(text) click to toggle source

Obfuscate the text by using HTML entities.

    # File lib/kramdown/converter/html.rb
443 def obfuscate(text)
444   result = ""
445   text.each_byte do |b|
446     result << (b > 128 ? b.chr : "&#%03d;" % b)
447   end
448   result.force_encoding(text.encoding)
449   result
450 end
remove_footnotes(elements) click to toggle source

Remove all footnotes from the given elements.

    # File lib/kramdown/converter/html.rb
435 def remove_footnotes(elements)
436   elements.delete_if do |c|
437     remove_footnotes(c.children)
438     c.type == :footnote
439   end
440 end