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
Initialize the LaTeX converter with the root
element and the conversion options
.
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
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
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
# 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
# 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
# File lib/kramdown/converter/latex.rb 60 def convert_blank(_el, opts) 61 opts[:result] =~ /\n\n\Z|\A\Z/ ? "" : "\n" 62 end
# 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
# 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
# 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
# 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
# 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
# File lib/kramdown/converter/latex.rb 150 def convert_dd(el, opts) 151 "#{latex_link_target(el)}#{inner(el, opts)}\n\n" 152 end
# File lib/kramdown/converter/latex.rb 138 def convert_dl(el, opts) 139 latex_environment('description', el, inner(el, opts)) 140 end
# File lib/kramdown/converter/latex.rb 146 def convert_dt(el, opts) 147 "\\item[#{inner(el, opts)}] " 148 end
# File lib/kramdown/converter/latex.rb 262 def convert_em(el, opts) 263 "\\emph{#{latex_link_target(el)}#{inner(el, opts)}}" 264 end
# File lib/kramdown/converter/latex.rb 532 def convert_entity(el, _opts) 533 entity_to_latex(el.value) 534 end
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# File lib/kramdown/converter/latex.rb 56 def convert_root(el, opts) 57 inner(el, opts) 58 end
# 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
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
# File lib/kramdown/converter/latex.rb 266 def convert_strong(el, opts) 267 "\\textbf{#{latex_link_target(el)}#{inner(el, opts)}}" 268 end
# 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
# File lib/kramdown/converter/latex.rb 188 def convert_tbody(el, opts) 189 inner(el, opts) 190 end
# File lib/kramdown/converter/latex.rb 200 def convert_td(el, opts) 201 inner(el, opts) 202 end
# File lib/kramdown/converter/latex.rb 64 def convert_text(el, _opts) 65 escape(el.value) 66 end
# File lib/kramdown/converter/latex.rb 192 def convert_tfoot(el, opts) 193 "\\hline \\hline \n#{inner(el, opts)}" 194 end
# File lib/kramdown/converter/latex.rb 184 def convert_thead(el, opts) 185 "#{inner(el, opts)}\\hline\n" 186 end
# 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
# 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
# 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
# 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
# File lib/kramdown/converter/latex.rb 169 def convert_xml_pi(_el, _opts) 170 warning("Can't convert XML PI") 171 '' 172 end
# 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 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
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
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
Return a string containing a valid hypertarget command if the element has an ID defined, or nil
otherwise. If the parameter add_label
is true
, a label command will also be used additionally to the hypertarget command.
# File lib/kramdown/converter/latex.rb 589 def latex_link_target(el, add_label = false) 590 if (id = el.attr['id']) 591 "\\hypertarget{#{id}}{}#{add_label ? "\\label{#{id}}" : ''}" 592 else 593 nil 594 end 595 end
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