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
599 def attribute_list(el)
600   attrs = el.attr.map {|k, v| v.nil? ? '' : " #{k}=\"#{v}\"" }.compact.sort.join
601   attrs = "   % #{attrs}" unless attrs.empty?
602   attrs
603 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
216 def convert_a(el, opts)
217   url = el.attr['href']
218   if url.start_with?('#')
219     "\\hyperlink{#{url[1..-1].gsub('%', '\\%')}}{#{inner(el, opts)}}"
220   else
221     "\\href{#{url.gsub('%', '\\%')}}{#{inner(el, opts)}}"
222   end
223 end
convert_abbreviation(el, _opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
569 def convert_abbreviation(el, _opts)
570   @data[:packages] += %w[acronym]
571   "\\ac{#{normalize_abbreviation_key(el.value)}}"
572 end
convert_blank(_el, opts) click to toggle source
   # File lib/kramdown/converter/latex.rb
60 def convert_blank(_el, opts)
61   opts[:result].match?(/\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
209 def convert_br(_el, opts)
210   res = +"\\newline"
211   res << "\n" if (c = opts[:parent].children[opts[:index] + 1]) &&
212     (c.type != :text || c.value !~ /^\s*\n/)
213   res
214 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
239 def convert_codespan(el, _opts)
240   lang = extract_code_language(el.attr)
241   if @options[:syntax_highlighter] == :minted &&
242       (highlighted_code = highlight_code(el.value, lang, :span))
243     @data[:packages] << 'minted'
244     "#{latex_link_target(el)}#{highlighted_code}"
245   else
246     "\\texttt{#{latex_link_target(el)}#{escape(el.value)}}"
247   end
248 end
convert_comment(el, _opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
205 def convert_comment(el, _opts)
206   el.value.split("\n").map {|l| "% #{l}" }.join("\n") << "\n"
207 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
263 def convert_em(el, opts)
264   "\\emph{#{latex_link_target(el)}#{inner(el, opts)}}"
265 end
convert_entity(el, _opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
533 def convert_entity(el, _opts)
534   entity_to_latex(el.value)
535 end
convert_footnote(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
250 def convert_footnote(el, opts)
251   @data[:packages] << 'fancyvrb'
252   "\\footnote{#{inner(el.value, opts).rstrip}}"
253 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   case el.value
156   when 'i', 'em'
157     "\\emph{#{inner(el, opts)}}"
158   when 'b', 'strong'
159     "\\textbf{#{inner(el, opts)}}"
160   else
161     warning("Can't convert HTML element")
162     ''
163   end
164 end
convert_img(el, _opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
225 def convert_img(el, _opts)
226   line = el.options[:location]
227   if el.attr['src'].match?(/^(https?|ftps?):\/\//)
228     warning("Cannot include non-local image#{line ? " (line #{line})" : ''}")
229     ''
230   elsif !el.attr['src'].empty?
231     @data[:packages] << 'graphicx'
232     "#{latex_link_target(el)}\\includegraphics{#{el.attr['src']}}"
233   else
234     warning("Cannot include image with empty path#{line ? " (line #{line})" : ''}")
235     ''
236   end
237 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
556 def convert_math(el, _opts)
557   @data[:packages] += %w[amssymb amsmath amsthm amsfonts]
558   if el.options[:category] == :block
559     if el.value.match?(/\A\s*\\begin\{/)
560       el.value
561     else
562       latex_environment('displaymath', el, el.value)
563     end
564   else
565     "$#{el.value}$"
566   end
567 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
255 def convert_raw(el, _opts)
256   if !el.options[:type] || el.options[:type].empty? || el.options[:type].include?('latex')
257     el.value + (el.options[:category] == :block ? "\n" : '')
258   else
259     ''
260   end
261 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
550 def convert_smart_quote(el, opts)
551   res = entity_to_latex(smart_quote_entity(el)).chomp('{}')
552   res << "{}" if ((nel = opts[:parent].children[opts[:index] + 1]) && nel.type == :smart_quote) || res =~ /\w$/
553   res
554 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
267 def convert_strong(el, opts)
268   "\\textbf{#{latex_link_target(el)}#{inner(el, opts)}}"
269 end
convert_table(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
177 def convert_table(el, opts)
178   @data[:packages] << 'longtable'
179   align = el.options[:alignment].map {|a| TABLE_ALIGNMENT_CHAR[a] }.join('|')
180   attrs = attribute_list(el)
181   "#{latex_link_target(el)}\\begin{longtable}{|#{align}|}#{attrs}\n" \
182     "\\hline\n#{inner(el, opts)}\\hline\n\\end{longtable}#{attrs}\n\n"
183 end
convert_tbody(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
189 def convert_tbody(el, opts)
190   inner(el, opts)
191 end
convert_td(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
201 def convert_td(el, opts)
202   inner(el, opts)
203 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
193 def convert_tfoot(el, opts)
194   "\\hline \\hline \n#{inner(el, opts)}"
195 end
convert_thead(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
185 def convert_thead(el, opts)
186   "#{inner(el, opts)}\\hline\n"
187 end
convert_tr(el, opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
197 def convert_tr(el, opts)
198   el.children.map {|c| send("convert_#{c.type}", c, opts) }.join(' & ') << "\\\\\n"
199 end
convert_typographic_sym(el, _opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
542 def convert_typographic_sym(el, _opts)
543   if (result = @options[:typographic_symbols][el.value])
544     escape(result)
545   else
546     TYPOGRAPHIC_SYMS[el.value]
547   end
548 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
166 def convert_xml_comment(el, _opts)
167   el.value.split("\n").map {|l| "% #{l}" }.join("\n") + "\n"
168 end
convert_xml_pi(_el, _opts) click to toggle source
    # File lib/kramdown/converter/latex.rb
170 def convert_xml_pi(_el, _opts)
171   warning("Can't convert XML PI")
172   ''
173 end
entity_to_latex(entity) click to toggle source
    # File lib/kramdown/converter/latex.rb
522 def entity_to_latex(entity)
523   text, package = ENTITY_CONV_TABLE[entity.code_point]
524   if text
525     @data[:packages] << package if package
526     text
527   else
528     warning("Couldn't find entity with code #{entity.code_point} in substitution table!")
529     ''
530   end
531 end
escape(str) click to toggle source

Escape the special LaTeX characters in the string str.

    # File lib/kramdown/converter/latex.rb
618 def escape(str)
619   str.gsub(ESCAPE_RE) {|m| ESCAPE_MAP[m] }
620 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
582 def latex_environment(type, el, text)
583   attrs = attribute_list(el)
584   "\\begin{#{type}}#{latex_link_target(el)}#{attrs}\n#{text.rstrip}\n\\end{#{type}}#{attrs}\n"
585 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
575 def normalize_abbreviation_key(key)
576   key.gsub(/\W/) {|m| m.unpack1('H*') }
577 end