# 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

FOOTNOTE_BACKLINK_FMT
ZERO_TO_ONETWENTYEIGHT

### 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
    49   # stash string representation of symbol to avoid allocations from multiple interpolations.
    50   @highlighter_class = " highlighter-#{options[:syntax_highlighter]}"
    51   @dispatcher = Hash.new {|h, k| h[k] = :"convert_#{k}" }
    52 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
    408 def add_syntax_highlighter_to_class_attr(attr, lang = nil)
    409   (attr['class'] = (attr['class'] || '') + @highlighter_class).lstrip!
    410   attr['class'].sub!(/\blanguage-\S+|(^)/) { "language-#{lang}#{$1 ? ' ' : ''}" } if lang
    411 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
    56 def convert(el, indent = -@indent)
    57   send(@dispatcher[el.type], el, indent)
    58 end

convert_a(el, indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    271 def convert_a(el, indent)
    272   format_as_span_html("a", el.attr, inner(el, indent))
    273 end

convert_abbreviation(el, _indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    364 def convert_abbreviation(el, _indent)
    365   title = @root.options[:abbrev_defs][el.value]
    366   attr = @root.options[:abbrev_attr][el.value].dup
    367   attr['title'] = title unless title.empty?
    368   format_as_span_html("abbr", attr, el.value)
    369 end

convert_blank(_el, _indent) click to toggle source

       # File lib/kramdown/converter/html.rb
    76 def convert_blank(_el, _indent)
    77   "\n"
    78 end

convert_blockquote(el, indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    140 def convert_blockquote(el, indent)
    141   format_as_indented_block_html("blockquote", el.attr, inner(el, indent), indent)
    142 end

convert_br(_el, _indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    267 def convert_br(_el, _indent)
    268   "<br />"
    269 end

convert_codeblock(el, indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    110 def convert_codeblock(el, indent)
    111   attr = el.attr.dup
    112   lang = extract_code_language!(attr)
    113   hl_opts = {}
    114   highlighted_code = highlight_code(el.value, el.options[:lang] || lang, :block, hl_opts)
    115
    116   if highlighted_code
    117     add_syntax_highlighter_to_class_attr(attr, lang || hl_opts[:default_lang])
    118     "#{' ' * indent}<div#{html_attributes(attr)}>#{highlighted_code}#{' ' * indent}</div>\n"
    119   else
    120     result = escape_html(el.value)
    121     result.chomp!
    122     if el.attr['class'].to_s =~ /\bshow-whitespaces\b/
    123       result.gsub!(/(?:(^[ \t]+)|([ \t]+$)|([ \t]+))/) do |m|
    124         suffix = ($1 ? '-l' : ($2 ? '-r' : ''))
    125         m.scan(/./).map do |c|
    126           case c
    127           when "\t" then "<span class=\"ws-tab#{suffix}\">\t</span>"
    128           when " " then "<span class=\"ws-space#{suffix}\">&#8901;</span>"
    129           end
    130         end.join('')
    131       end
    132     end
    133     code_attr = {}
    134     code_attr['class'] = "language-#{lang}" if lang
    135     "#{' ' * indent}<pre#{html_attributes(attr)}>" \
    136       "<code#{html_attributes(code_attr)}>#{result}\n</code></pre>\n"
    137   end
    138 end

convert_codespan(el, _indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    279 def convert_codespan(el, _indent)
    280   attr = el.attr.dup
    281   lang = extract_code_language(attr)
    282   hl_opts = {}
    283   result = highlight_code(el.value, lang, :span, hl_opts)
    284   if result
    285     add_syntax_highlighter_to_class_attr(attr, lang || hl_opts[:default_lang])
    286   else
    287     result = escape_html(el.value)
    288   end
    289
    290   format_as_span_html('code', attr, result)
    291 end

convert_comment(el, indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    259 def convert_comment(el, indent)
    260   if el.options[:category] == :block
    261     "#{' ' * indent}<!-- #{el.value} -->\n"
    262   else
    263     "<!-- #{el.value} -->"
    264   end
    265 end

convert_dd(el, indent)
Alias for: convert_li
convert_dl(el, indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    173 def convert_dl(el, indent)
    174   format_as_indented_block_html("dl", el.attr, inner(el, indent), indent)
    175 end

convert_dt(el, indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    189 def convert_dt(el, indent)
    190   attr = el.attr.dup
    191   @stack.last.options[:ial][:refs].each do |ref|
    192     if ref =~ /\Aauto_ids(?:-([\w-]+))?/
    193       attr['id'] = "#{$1}#{basic_generate_id(el.options[:raw_text])}".lstrip
    194       break
    195     end
    196   end if !attr['id'] && @stack.last.options[:ial] && @stack.last.options[:ial][:refs]
    197   format_as_block_html("dt", attr, inner(el, indent), indent)
    198 end

convert_em(el, indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    318 def convert_em(el, indent)
    319   format_as_span_html(el.type, el.attr, inner(el, indent))
    320 end

Also aliased as: convert_strong
convert_entity(el, _indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    323 def convert_entity(el, _indent)
    324   entity_to_str(el.value, el.options[:original])
    325 end

convert_footnote(el, _indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    293 def convert_footnote(el, _indent)
    294   repeat = ''
    295   name = @options[:footnote_prefix] + el.options[:name]
    296   if (footnote = @footnotes_by_name[name])
    297     number = footnote[2]
    298     repeat = ":#{footnote[3] += 1}"
    299   else
    300     number = @footnote_counter
    301     @footnote_counter += 1
    302     @footnotes << [name, el.value, number, 0]
    303     @footnotes_by_name[name] = @footnotes.last
    304   end
    305   "<sup id=\"fnref:#{name}#{repeat}\" role=\"doc-noteref\">" \
    306     "<a href=\"#fn:#{name}\" class=\"footnote\" rel=\"footnote\">" \
    307     "#{number}</a></sup>"
    308 end

convert_header(el, indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    144 def convert_header(el, indent)
    145   attr = el.attr.dup
    146   if @options[:auto_ids] && !attr['id']
    147     attr['id'] = generate_id(el.options[:raw_text])
    148   end
    149   @toc << [el.options[:level], attr['id'], el.children] if attr['id'] && in_toc?(el)
    150   level = output_header_level(el.options[:level])
    151   format_as_block_html("h#{level}", attr, inner(el, indent), indent)
    152 end

convert_hr(el, indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    154 def convert_hr(el, indent)
    155   "#{' ' * indent}<hr#{html_attributes(el.attr)} />\n"
    156 end

convert_html_element(el, indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    200 def convert_html_element(el, indent)
    201   res = inner(el, indent)
    202   if el.options[:category] == :span
    203     "<#{el.value}#{html_attributes(el.attr)}" + \
    204       (res.empty? && HTML_ELEMENTS_WITHOUT_BODY.include?(el.value) ? " />" : ">#{res}</#{el.value}>")
    205   else
    206     output = +''
    207     if @stack.last.type != :html_element || @stack.last.options[:content_model] != :raw
    208       output << ' ' * indent
    209     end
    210     output << "<#{el.value}#{html_attributes(el.attr)}"
    211     if el.options[:is_closed] && el.options[:content_model] == :raw
    212       output << " />"
    213     elsif !res.empty? && el.options[:content_model] != :block
    214       output << ">#{res}</#{el.value}>"
    215     elsif !res.empty?
    216       output << ">\n#{res.chomp}\n" << ' ' * indent << "</#{el.value}>"
    217     elsif HTML_ELEMENTS_WITHOUT_BODY.include?(el.value)
    218       output << " />"
    219     else
    220       output << "></#{el.value}>"
    221     end
    222     output << "\n" if @stack.last.type != :html_element || @stack.last.options[:content_model] != :raw
    223     output
    224   end
    225 end

convert_img(el, _indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    275 def convert_img(el, _indent)
    276   "<img#{html_attributes(el.attr)} />"
    277 end

convert_li(el, indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    177 def convert_li(el, indent)
    178   output = ' ' * indent << "<#{el.type}" << html_attributes(el.attr) << ">"
    179   res = inner(el, indent)
    180   if el.children.empty? || (el.children.first.type == :p && el.children.first.options[:transparent])
    181     output << res << (res =~ /\n\Z/ ? ' ' * indent : '')
    182   else
    183     output << "\n" << res << ' ' * indent
    184   end
    185   output << "</#{el.type}>\n"
    186 end

Also aliased as: convert_dd
convert_math(el, indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    350 def convert_math(el, indent)
    351   if (result = format_math(el, indent: indent))
    352     result
    353   else
    354     attr = el.attr.dup
    355     attr['class'] = "#{attr['class']} kdmath".lstrip
    356     if el.options[:category] == :block
    357       format_as_block_html('div', attr, "$$\n#{el.value}\n$$", indent)
    358     else
    359       format_as_span_html('span', attr, "$#{el.value}$")
    360     end
    361   end
    362 end

convert_ol(el, indent)
Alias for: convert_ul
convert_p(el, indent) click to toggle source

       # File lib/kramdown/converter/html.rb
    85 def convert_p(el, indent)
    86   if el.options[:transparent]
    87     inner(el, indent)
    88   elsif el.children.size == 1 && el.children.first.type == :img &&
    89       el.children.first.options[:ial]&.[](:refs)&.include?('standalone')
    90     convert_standalone_image(el, indent)
    91   else
    92     format_as_block_html("p", el.attr, inner(el, indent), indent)
    93   end
    94 end

convert_raw(el, _indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    310 def convert_raw(el, _indent)
    311   if !el.options[:type] || el.options[:type].empty? || el.options[:type].include?('html')
    312     el.value + (el.options[:category] == :block ? "\n" : '')
    313   else
    314     ''
    315   end
    316 end

convert_root(el, indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    371 def convert_root(el, indent)
    372   result = inner(el, indent)
    373   if @footnote_location
    374     result.sub!(/#{@footnote_location}/, footnote_content.gsub(/\\/, "\\\\\\\\"))
    375   else
    376     result << footnote_content
    377   end
    378   if @toc_code
    379     toc_tree = generate_toc_tree(@toc, @toc_code[0], @toc_code[1] || {})
    380     text = if !toc_tree.children.empty?
    381              convert(toc_tree, 0)
    382            else
    383              ''
    384            end
    385     result.sub!(/#{@toc_code.last}/, text.gsub(/\\/, "\\\\\\\\"))
    386   end
    387   result
    388 end

convert_smart_quote(el, _indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    346 def convert_smart_quote(el, _indent)
    347   entity_to_str(smart_quote_entity(el))
    348 end

convert_standalone_image(el, indent) click to toggle source

Helper method used by `convert_p` to convert a paragraph that only contains a single :img element.

        # File lib/kramdown/converter/html.rb
     98 def convert_standalone_image(el, indent)
     99   figure_attr = el.attr.dup
    100   image_attr = el.children.first.attr.dup
    101
    102   figure_attr['class'] = image_attr.delete('class') if image_attr.key?('class') and not figure_attr.key?('class')
    103   figure_attr['id'] = image_attr.delete('id') if image_attr.key?('id') and not figure_attr.key?('id')
    104
    105   body = "#{' ' * (indent + @indent)}<img#{html_attributes(image_attr)} />\n" \
    106     "#{' ' * (indent + @indent)}<figcaption>#{image_attr['alt']}</figcaption>\n"
    107   format_as_indented_block_html("figure", figure_attr, body, indent)
    108 end

convert_strong(el, indent)
Alias for: convert_em
convert_table(el, indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    237 def convert_table(el, indent)
    238   format_as_indented_block_html(el.type, el.attr, inner(el, indent), indent)
    239 end

Also aliased as: convert_thead, convert_tbody, convert_tfoot, convert_tr
convert_tbody(el, indent)
Alias for: convert_table
convert_td(el, indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    247 def convert_td(el, indent)
    248   res = inner(el, indent)
    249   type = (@stack[-2].type == :thead ? :th : :td)
    250   attr = el.attr
    251   alignment = @stack[-3].options[:alignment][@stack.last.children.index(el)]
    252   if alignment != :default
    253     attr = el.attr.dup
    254     attr['style'] = (attr.key?('style') ? "#{attr['style']}; " : '') + "text-align: #{alignment}"
    255   end
    256   format_as_block_html(type, attr, res.empty? ? entity_to_str(ENTITY_NBSP) : res, indent)
    257 end

convert_text(el, _indent) click to toggle source

       # File lib/kramdown/converter/html.rb
    80 def convert_text(el, _indent)
    81   escaped = escape_html(el.value, :text)
    82   @options[:remove_line_breaks_for_cjk] ? fix_cjk_line_break(escaped) : escaped
    83 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
    338 def convert_typographic_sym(el, _indent)
    339   if (result = @options[:typographic_symbols][el.value])
    340     escape_html(result, :text)
    341   else
    342     TYPOGRAPHIC_SYMS[el.value].map {|e| entity_to_str(e) }.join('')
    343   end
    344 end

convert_ul(el, indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    161 def convert_ul(el, indent)
    162   if !@toc_code && el.options.dig(:ial, :refs)&.include?('toc')
    163     @toc_code = [el.type, el.attr, ZERO_TO_ONETWENTYEIGHT.map { rand(36).to_s(36) }.join]
    164     @toc_code.last
    165   elsif !@footnote_location && el.options.dig(:ial, :refs)&.include?('footnotes')
    166     @footnote_location = ZERO_TO_ONETWENTYEIGHT.map { rand(36).to_s(36) }.join
    167   else
    168     format_as_indented_block_html(el.type, el.attr, inner(el, indent), indent)
    169   end
    170 end

Also aliased as: convert_ol
convert_xml_comment(el, indent) click to toggle source

        # File lib/kramdown/converter/html.rb
    227 def convert_xml_comment(el, indent)
    228   if el.options[:category] == :block &&
    229       (@stack.last.type != :html_element || @stack.last.options[:content_model] != :raw)
    230     ' ' * indent << el.value << "\n"
    231   else
    232     el.value
    233   end
    234 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
    452 def fix_for_toc_entry(elements)
    453   remove_footnotes(elements)
    454   unwrap_links(elements)
    455   elements
    456 end

footnote_content() click to toggle source

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

        # File lib/kramdown/converter/html.rb
    487 def footnote_content
    488   ol = Element.new(:ol)
    489   ol.attr['start'] = @footnote_start if @footnote_start != 1
    490   i = 0
    491   backlink_text = escape_html(@options[:footnote_backlink], :text)
    492   while i < @footnotes.length
    493     name, data, _, repeat = *@footnotes[i]
    494     li = Element.new(:li, nil, 'id' => "fn:#{name}", 'role' => 'doc-endnote')
    495     li.children = Marshal.load(Marshal.dump(data.children))
    496
    497     para = nil
    498     if li.children.last.type == :p || @options[:footnote_backlink_inline]
    499       parent = li
    500       while !parent.children.empty? && ![:p, :header].include?(parent.children.last.type)
    501         parent = parent.children.last
    502       end
    503       para = parent.children.last
    504       insert_space = true
    505     end
    506
    507     unless para
    508       li.children << (para = Element.new(:p))
    509       insert_space = false
    510     end
    511
    512     unless @options[:footnote_backlink].empty?
    513       nbsp = entity_to_str(ENTITY_NBSP)
    514       value = sprintf(FOOTNOTE_BACKLINK_FMT, (insert_space ? nbsp : ''), name, backlink_text)
    515       para.children << Element.new(:raw, value)
    516       (1..repeat).each do |index|
    517         value = sprintf(FOOTNOTE_BACKLINK_FMT, nbsp, "#{name}:#{index}",
    518                         "#{backlink_text}<sup>#{index + 1}</sup>")
    519         para.children << Element.new(:raw, value)
    520       end
    521     end
    522
    523     ol.children << Element.new(:raw, convert(li, 4))
    524     i += 1
    525   end
    526   if ol.children.empty?
    527     ''
    528   else
    529     format_as_indented_block_html('div', {class: "footnotes", role: "doc-endnotes"}, convert(ol, 2), 0)
    530   end
    531 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
    396 def format_as_block_html(name, attr, body, indent)
    397   "#{' ' * indent}<#{name}#{html_attributes(attr)}>#{body}</#{name}>\n"
    398 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
    402 def format_as_indented_block_html(name, attr, body, indent)
    403   "#{' ' * indent}<#{name}#{html_attributes(attr)}>\n#{body}#{' ' * indent}</#{name}>\n"
    404 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
    391 def format_as_span_html(name, attr, body)
    392   "<#{name}#{html_attributes(attr)}>#{body}</#{name}>"
    393 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
    65 def inner(el, indent)
    66   result = +''
    67   indent += @indent
    68   @stack.push(el)
    69   el.children.each do |inner_el|
    70     result << send(@dispatcher[inner_el.type], inner_el, indent)
    71   end
    72   @stack.pop
    73   result
    74 end

obfuscate(text) click to toggle source

Obfuscate the `text` by using HTML entities.

        # File lib/kramdown/converter/html.rb
    475 def obfuscate(text)
    476   result = +''
    477   text.each_byte do |b|
    478     result << (b > 128 ? b.chr : sprintf("&#%03d;", b))
    479   end
    480   result.force_encoding(text.encoding)
    481   result
    482 end

remove_footnotes(elements) click to toggle source

Remove all footnotes from the given elements.

        # File lib/kramdown/converter/html.rb
    467 def remove_footnotes(elements)
    468   elements.delete_if do |c|
    469     remove_footnotes(c.children)
    470     c.type == :footnote
    471   end
    472 end

unwrap_links(elements) click to toggle source

Remove all link elements by unwrapping them.

        # File lib/kramdown/converter/html.rb
    459 def unwrap_links(elements)
    460   elements.map! do |c|
    461     unwrap_links(c.children)
    462     c.type == :a ? c.children : c
    463   end.flatten!
    464 end