class Kramdown::Converter::Latex

Converts an element tree to LaTeX.

This converter uses ideas from other Markdown-to-LaTeX converters like Pandoc and Maruku.

You can customize this 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.

opts

A hash containing processing options that are passed down from parent elements. The key :parent is always set and contains the parent element as value.

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

Public Class Methods

new(root, options) click to toggle source

Initialize the LaTeX converter with the root element and the conversion options.

Calls superclass method Kramdown::Converter::Base::new
   # File lib/kramdown/converter/latex.rb
33 def initialize(root, options)
34   super
35   @data[:packages] = Set.new
36 end

Public Instance Methods

attribute_list(el) click to toggle source

Return a LaTeX comment containing all attributes as ‘key=“value”’ pairs.

    # File lib/kramdown/converter/latex.rb
598 def attribute_list(el)
599   attrs = el.attr.map {|k, v| v.nil? ? '' : " #{k}=\"#{v}\"" }.compact.sort.join('')
600   attrs = "   % #{attrs}" unless attrs.empty?
601   attrs
602 end
convert(el, opts = {}) 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/latex.rb
40 def convert(el, opts = {})
41   send("convert_#{el.type}", el, opts)
42 end
convert_a(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
215 def convert_a(el, opts)
216   url = el.attr['href']
217   if url.start_with?('#')
218     "\\hyperlink{#{url[1..-1].gsub('%', '\\%')}}{#{inner(el, opts)}}"
219   else
220     "\\href{#{url.gsub('%', '\\%')}}{#{inner(el, opts)}}"
221   end
222 end
convert_abbreviation(el, _opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
568 def convert_abbreviation(el, _opts)
569   @data[:packages] += %w[acronym]
570   "\\ac{#{normalize_abbreviation_key(el.value)}}"
571 end
convert_blank(_el, opts) click to toggle source
   # File lib/kramdown/converter/latex.rb
60 def convert_blank(_el, opts)
61   opts[:result] =~ /\n\n\Z|\A\Z/ ? "" : "\n"
62 end
convert_blockquote(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
109 def convert_blockquote(el, opts)
110   latex_environment(el.children.size > 1 ? 'quotation' : 'quote', el, inner(el, opts))
111 end
convert_br(_el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
208 def convert_br(_el, opts)
209   res = +"\\newline"
210   res << "\n" if (c = opts[:parent].children[opts[:index] + 1]) &&
211     (c.type != :text || c.value !~ /^\s*\n/)
212   res
213 end
convert_codeblock(el, _opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
 86 def convert_codeblock(el, _opts)
 87   show_whitespace = el.attr['class'].to_s =~ /\bshow-whitespaces\b/
 88   lang = extract_code_language(el.attr)
 89 
 90   if @options[:syntax_highlighter] == :minted &&
 91       (highlighted_code = highlight_code(el.value, lang, :block))
 92     @data[:packages] << 'minted'
 93     "#{latex_link_target(el)}#{highlighted_code}\n"
 94   elsif show_whitespace || lang
 95     options = []
 96     options << (show_whitespace ? "showspaces=true,showtabs=true" : "showspaces=false,showtabs=false")
 97     options << "language=#{lang}" if lang
 98     options << "basicstyle=\\ttfamily\\footnotesize,columns=fixed,frame=tlbr"
 99     id = el.attr['id']
100     options << "label=#{id}" if id
101     attrs = attribute_list(el)
102     "#{latex_link_target(el)}\\begin{lstlisting}[#{options.join(',')}]\n" \
103       "#{el.value}\n\\end{lstlisting}#{attrs}\n"
104   else
105     "#{latex_link_target(el)}\\begin{verbatim}#{el.value}\\end{verbatim}\n"
106   end
107 end
convert_codespan(el, _opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
238 def convert_codespan(el, _opts)
239   lang = extract_code_language(el.attr)
240   if @options[:syntax_highlighter] == :minted &&
241       (highlighted_code = highlight_code(el.value, lang, :span))
242     @data[:packages] << 'minted'
243     "#{latex_link_target(el)}#{highlighted_code}"
244   else
245     "\\texttt{#{latex_link_target(el)}#{escape(el.value)}}"
246   end
247 end
convert_comment(el, _opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
204 def convert_comment(el, _opts)
205   el.value.split(/\n/).map {|l| "% #{l}" }.join("\n") << "\n"
206 end
convert_dd(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
150 def convert_dd(el, opts)
151   "#{latex_link_target(el)}#{inner(el, opts)}\n\n"
152 end
convert_dl(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
138 def convert_dl(el, opts)
139   latex_environment('description', el, inner(el, opts))
140 end
convert_dt(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
146 def convert_dt(el, opts)
147   "\\item[#{inner(el, opts)}] "
148 end
convert_em(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
262 def convert_em(el, opts)
263   "\\emph{#{latex_link_target(el)}#{inner(el, opts)}}"
264 end
convert_entity(el, _opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
532 def convert_entity(el, _opts)
533   entity_to_latex(el.value)
534 end
convert_footnote(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
249 def convert_footnote(el, opts)
250   @data[:packages] << 'fancyvrb'
251   "\\footnote{#{inner(el.value, opts).rstrip}}"
252 end
convert_header(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
113 def convert_header(el, opts)
114   type = @options[:latex_headers][output_header_level(el.options[:level]) - 1]
115   if ((id = el.attr['id']) ||
116       (@options[:auto_ids] && (id = generate_id(el.options[:raw_text])))) && in_toc?(el)
117     "\\#{type}{#{inner(el, opts)}}\\hypertarget{#{id}}{}\\label{#{id}}\n\n"
118   else
119     "\\#{type}*{#{inner(el, opts)}}\n\n"
120   end
121 end
convert_hr(el, _opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
123 def convert_hr(el, _opts)
124   attrs = attribute_list(el)
125   "#{latex_link_target(el)}\\begin{center}#{attrs}\n\\rule{3in}{0.4pt}\n\\end{center}#{attrs}\n"
126 end
convert_html_element(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
154 def convert_html_element(el, opts)
155   if el.value == 'i' || el.value == 'em'
156     "\\emph{#{inner(el, opts)}}"
157   elsif el.value == 'b' || el.value == 'strong'
158     "\\textbf{#{inner(el, opts)}}"
159   else
160     warning("Can't convert HTML element")
161     ''
162   end
163 end
convert_img(el, _opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
224 def convert_img(el, _opts)
225   line = el.options[:location]
226   if el.attr['src'] =~ /^(https?|ftps?):\/\//
227     warning("Cannot include non-local image#{line ? " (line #{line})" : ''}")
228     ''
229   elsif !el.attr['src'].empty?
230     @data[:packages] << 'graphicx'
231     "#{latex_link_target(el)}\\includegraphics{#{el.attr['src']}}"
232   else
233     warning("Cannot include image with empty path#{line ? " (line #{line})" : ''}")
234     ''
235   end
236 end
convert_li(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
142 def convert_li(el, opts)
143   "\\item{} #{latex_link_target(el, true)}#{inner(el, opts).sub(/\n+\Z/, '')}\n"
144 end
convert_math(el, _opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
555 def convert_math(el, _opts)
556   @data[:packages] += %w[amssymb amsmath amsthm amsfonts]
557   if el.options[:category] == :block
558     if el.value =~ /\A\s*\\begin\{/
559       el.value
560     else
561       latex_environment('displaymath', el, el.value)
562     end
563   else
564     "$#{el.value}$"
565   end
566 end
convert_ol(el, opts)
Alias for: convert_ul
convert_p(el, opts) click to toggle source
   # File lib/kramdown/converter/latex.rb
68 def convert_p(el, opts)
69   if el.children.size == 1 && el.children.first.type == :img &&
70       !(img = convert_img(el.children.first, opts)).empty?
71     convert_standalone_image(el, opts, img)
72   else
73     "#{latex_link_target(el)}#{inner(el, opts)}\n\n"
74   end
75 end
convert_raw(el, _opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
254 def convert_raw(el, _opts)
255   if !el.options[:type] || el.options[:type].empty? || el.options[:type].include?('latex')
256     el.value + (el.options[:category] == :block ? "\n" : '')
257   else
258     ''
259   end
260 end
convert_root(el, opts) click to toggle source
   # File lib/kramdown/converter/latex.rb
56 def convert_root(el, opts)
57   inner(el, opts)
58 end
convert_smart_quote(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
549 def convert_smart_quote(el, opts)
550   res = entity_to_latex(smart_quote_entity(el)).chomp('{}')
551   res << "{}" if ((nel = opts[:parent].children[opts[:index] + 1]) && nel.type == :smart_quote) || res =~ /\w$/
552   res
553 end
convert_standalone_image(el, _opts, img) 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/latex.rb
79 def convert_standalone_image(el, _opts, img)
80   attrs = attribute_list(el)
81   "\\begin{figure}#{attrs}\n\\begin{center}\n#{img}\n\\end{center}\n" \
82     "\\caption{#{escape(el.children.first.attr['alt'])}}\n" \
83     "#{latex_link_target(el, true)}\n\\end{figure}#{attrs}\n"
84 end
convert_strong(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
266 def convert_strong(el, opts)
267   "\\textbf{#{latex_link_target(el)}#{inner(el, opts)}}"
268 end
convert_table(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
176 def convert_table(el, opts)
177   @data[:packages] << 'longtable'
178   align = el.options[:alignment].map {|a| TABLE_ALIGNMENT_CHAR[a] }.join('|')
179   attrs = attribute_list(el)
180   "#{latex_link_target(el)}\\begin{longtable}{|#{align}|}#{attrs}\n" \
181     "\\hline\n#{inner(el, opts)}\\hline\n\\end{longtable}#{attrs}\n\n"
182 end
convert_tbody(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
188 def convert_tbody(el, opts)
189   inner(el, opts)
190 end
convert_td(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
200 def convert_td(el, opts)
201   inner(el, opts)
202 end
convert_text(el, _opts) click to toggle source
   # File lib/kramdown/converter/latex.rb
64 def convert_text(el, _opts)
65   escape(el.value)
66 end
convert_tfoot(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
192 def convert_tfoot(el, opts)
193   "\\hline \\hline \n#{inner(el, opts)}"
194 end
convert_thead(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
184 def convert_thead(el, opts)
185   "#{inner(el, opts)}\\hline\n"
186 end
convert_tr(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
196 def convert_tr(el, opts)
197   el.children.map {|c| send("convert_#{c.type}", c, opts) }.join(' & ') << "\\\\\n"
198 end
convert_typographic_sym(el, _opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
541 def convert_typographic_sym(el, _opts)
542   if (result = @options[:typographic_symbols][el.value])
543     escape(result)
544   else
545     TYPOGRAPHIC_SYMS[el.value]
546   end
547 end
convert_ul(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
128 def convert_ul(el, opts)
129   if !@data[:has_toc] && el.options.dig(:ial, :refs)&.include?('toc')
130     @data[:has_toc] = true
131     '\tableofcontents'
132   else
133     latex_environment(el.type == :ul ? 'itemize' : 'enumerate', el, inner(el, opts))
134   end
135 end
Also aliased as: convert_ol
convert_xml_comment(el, _opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
165 def convert_xml_comment(el, _opts)
166   el.value.split(/\n/).map {|l| "% #{l}" }.join("\n") + "\n"
167 end
convert_xml_pi(_el, _opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
169 def convert_xml_pi(_el, _opts)
170   warning("Can't convert XML PI")
171   ''
172 end
entity_to_latex(entity) click to toggle source
    # File lib/kramdown/converter/latex.rb
521 def entity_to_latex(entity)
522   text, package = ENTITY_CONV_TABLE[entity.code_point]
523   if text
524     @data[:packages] << package if package
525     text
526   else
527     warning("Couldn't find entity with code #{entity.code_point} in substitution table!")
528     ''
529   end
530 end
escape(str) click to toggle source

Escape the special LaTeX characters in the string str.

    # File lib/kramdown/converter/latex.rb
617 def escape(str)
618   str.gsub(ESCAPE_RE) {|m| ESCAPE_MAP[m] }
619 end
inner(el, opts) click to toggle source

Return the converted content of the children of el as a string.

   # File lib/kramdown/converter/latex.rb
45 def inner(el, opts)
46   result = +''
47   options = opts.dup.merge(parent: el)
48   el.children.each_with_index do |inner_el, index|
49     options[:index] = index
50     options[:result] = result
51     result << send("convert_#{inner_el.type}", inner_el, options)
52   end
53   result
54 end
latex_environment(type, el, text) click to toggle source

Wrap the text inside a LaTeX environment of type type. The element el is passed on to the method attribute_list – the resulting string is appended to both the \begin and the \end lines of the LaTeX environment for easier post-processing of LaTeX environments.

    # File lib/kramdown/converter/latex.rb
581 def latex_environment(type, el, text)
582   attrs = attribute_list(el)
583   "\\begin{#{type}}#{latex_link_target(el)}#{attrs}\n#{text.rstrip}\n\\end{#{type}}#{attrs}\n"
584 end
normalize_abbreviation_key(key) click to toggle source

Normalize the abbreviation key so that it only contains allowed ASCII character

    # File lib/kramdown/converter/latex.rb
574 def normalize_abbreviation_key(key)
575   key.gsub(/\W/) {|m| m.unpack('H*').first }
576 end