Class | RDoc::Markup::ToLaTeX |
In: |
markup/to_latex.rb
|
Parent: | RDoc::Markup::Formatter |
Convert SimpleMarkup to basic LaTeX report format.
# File markup/to_latex.rb, line 24 24: def self.l(str) 25: str.tr('\\', BS).tr('{', OB).tr('}', CB).tr('$', DL) 26: end
# File markup/to_latex.rb, line 45 45: def initialize 46: init_tags 47: @list_depth = 0 48: @prev_list_types = [] 49: end
# File markup/to_latex.rb, line 139 139: def accept_blank_line(am, fragment) 140: # @res << "\n" 141: end
# File markup/to_latex.rb, line 143 143: def accept_heading(am, fragment) 144: @res << convert_heading(fragment.head_level, am.flow(fragment.txt)) 145: end
# File markup/to_latex.rb, line 123 123: def accept_list_end(am, fragment) 124: if tag = @in_list_entry.pop 125: @res << tag << "\n" 126: end 127: @res << list_name(fragment.type, false) << "\n" 128: end
# File markup/to_latex.rb, line 130 130: def accept_list_item(am, fragment) 131: if tag = @in_list_entry.last 132: @res << tag << "\n" 133: end 134: @res << list_item_start(am, fragment) 135: @res << wrap(convert_flow(am.flow(fragment.txt))) << "\n" 136: @in_list_entry[-1] = list_end_for(fragment.type) 137: end
# File markup/to_latex.rb, line 118 118: def accept_list_start(am, fragment) 119: @res << list_name(fragment.type, true) << "\n" 120: @in_list_entry.push false 121: end
# File markup/to_latex.rb, line 101 101: def accept_paragraph(am, fragment) 102: @res << wrap(convert_flow(am.flow(fragment.txt))) 103: @res << "\n" 104: end
# File markup/to_latex.rb, line 112 112: def accept_rule(am, fragment) 113: size = fragment.param 114: size = 10 if size > 10 115: @res << "\n\n\\rule{\\linewidth}{#{size}pt}\n\n" 116: end
# File markup/to_latex.rb, line 106 106: def accept_verbatim(am, fragment) 107: @res << "\n\\begin{code}\n" 108: @res << fragment.txt.sub(/[\n\s]+\Z/, '') 109: @res << "\n\\end{code}\n\n" 110: end
# File markup/to_latex.rb, line 201 201: def convert_flow(flow) 202: res = "" 203: flow.each do |item| 204: case item 205: when String 206: $stderr.puts "Converting '#{item}'" if $DEBUG_RDOC 207: res << convert_string(item) 208: when AttrChanger 209: off_tags(res, item) 210: on_tags(res, item) 211: when Special 212: res << convert_special(item) 213: else 214: raise "Unknown flow element: #{item.inspect}" 215: end 216: end 217: res 218: end
# File markup/to_latex.rb, line 260 260: def convert_heading(level, flow) 261: res = 262: case level 263: when 1 then "\\chapter{" 264: when 2 then "\\section{" 265: when 3 then "\\subsection{" 266: when 4 then "\\subsubsection{" 267: else "\\paragraph{" 268: end + 269: convert_flow(flow) + 270: "}\n" 271: end
# File markup/to_latex.rb, line 247 247: def convert_special(special) 248: handled = false 249: Attribute.each_name_of(special.type) do |name| 250: method_name = "handle_special_#{name}" 251: if self.respond_to? method_name 252: special.text = send(method_name, special) 253: handled = true 254: end 255: end 256: raise "Unhandled special: #{special}" unless handled 257: special.text 258: end
some of these patterns are taken from SmartyPants...
# File markup/to_latex.rb, line 223 223: def convert_string(item) 224: escape(item). 225: 226: # convert ... to elipsis (and make sure .... becomes .<elipsis>) 227: gsub(/\.\.\.\./, '.\ldots{}').gsub(/\.\.\./, '\ldots{}'). 228: 229: # convert single closing quote 230: gsub(%r{([^ \t\r\n\[\{\(])\'}, '\1\''). 231: gsub(%r{\'(?=\W|s\b)}, "'" ). 232: 233: # convert single opening quote 234: gsub(/'/, '`'). 235: 236: # convert double closing quote 237: gsub(%r{([^ \t\r\n\[\{\(])\"(?=\W)}, "\\1''"). 238: 239: # convert double opening quote 240: gsub(/"/, "``"). 241: 242: # convert copyright 243: gsub(/\(c\)/, '\ccopyright{}') 244: 245: end
# File markup/to_latex.rb, line 97 97: def end_accepting 98: @res.tr(BS, '\\').tr(OB, '{').tr(CB, '}').tr(DL, '$') 99: end
Escape a LaTeX string
# File markup/to_latex.rb, line 65 65: def escape(str) 66: $stderr.print "FE: ", str if $DEBUG_RDOC 67: s = str. 68: sub(/\s+$/, ''). 69: gsub(/([_\${}&%#])/, "#{BS}\\1"). 70: gsub(/\\/, BACKSLASH). 71: gsub(/\^/, HAT). 72: gsub(/~/, TILDE). 73: gsub(/</, LESSTHAN). 74: gsub(/>/, GREATERTHAN). 75: gsub(/,,/, ",{},"). 76: gsub(/\`/, BACKQUOTE) 77: $stderr.print "-> ", s, "\n" if $DEBUG_RDOC 78: s 79: end
Set up the standard mapping of attributes to LaTeX
# File markup/to_latex.rb, line 54 54: def init_tags 55: @attr_tags = [ 56: InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:BOLD), l("\\textbf{"), l("}")), 57: InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:TT), l("\\texttt{"), l("}")), 58: InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:EM), l("\\emph{"), l("}")), 59: ] 60: end
# File markup/to_latex.rb, line 316 316: def list_end_for(fragment_type) 317: case fragment_type 318: when :BULLET, :NUMBER, :UPPERALPHA, :LOWERALPHA, :LABELED then 319: "" 320: when :NOTE 321: "\\\\\n" 322: else 323: raise "Invalid list type" 324: end 325: end
# File markup/to_latex.rb, line 301 301: def list_item_start(am, fragment) 302: case fragment.type 303: when :BULLET, :NUMBER, :UPPERALPHA, :LOWERALPHA then 304: "\\item " 305: 306: when :LABELED then 307: "\\item[" + convert_flow(am.flow(fragment.param)) + "] " 308: 309: when :NOTE then 310: convert_flow(am.flow(fragment.param)) + " & " 311: else 312: raise "Invalid list type" 313: end 314: end
# File markup/to_latex.rb, line 273 273: def list_name(list_type, is_open_tag) 274: tags = LIST_TYPE_TO_LATEX[list_type] || raise("Invalid list type: #{list_type.inspect}") 275: if tags[2] # enumerate 276: if is_open_tag 277: @list_depth += 1 278: if @prev_list_types[@list_depth] != tags[2] 279: case @list_depth 280: when 1 281: roman = "i" 282: when 2 283: roman = "ii" 284: when 3 285: roman = "iii" 286: when 4 287: roman = "iv" 288: else 289: raise("Too deep list: level #{@list_depth}") 290: end 291: @prev_list_types[@list_depth] = tags[2] 292: return l("\\renewcommand{\\labelenum#{roman}}{#{tags[2]}{enum#{roman}}}") + "\n" + tags[0] 293: end 294: else 295: @list_depth -= 1 296: end 297: end 298: tags[ is_open_tag ? 0 : 1] 299: end
# File markup/to_latex.rb, line 190 190: def off_tags(res, item) 191: attr_mask = item.turn_off 192: return if attr_mask.zero? 193: 194: @attr_tags.reverse_each do |tag| 195: if attr_mask & tag.bit != 0 196: res << tag.off 197: end 198: end 199: end
# File markup/to_latex.rb, line 179 179: def on_tags(res, item) 180: attr_mask = item.turn_on 181: return if attr_mask.zero? 182: 183: @attr_tags.each do |tag| 184: if attr_mask & tag.bit != 0 185: res << tag.on 186: end 187: end 188: end
Here‘s the client side of the visitor pattern
# File markup/to_latex.rb, line 92 92: def start_accepting 93: @res = "" 94: @in_list_entry = [] 95: end
This is a higher speed (if messier) version of wrap
# File markup/to_latex.rb, line 150 150: def wrap(txt, line_len = 76) 151: res = "" 152: sp = 0 153: ep = txt.length 154: while sp < ep 155: # scan back for a space 156: p = sp + line_len - 1 157: if p >= ep 158: p = ep 159: else 160: while p > sp and txt[p] != ?\s 161: p -= 1 162: end 163: if p <= sp 164: p = sp + line_len 165: while p < ep and txt[p] != ?\s 166: p += 1 167: end 168: end 169: end 170: res << txt[sp...p] << "\n" 171: sp = p 172: sp += 1 while sp < ep and txt[sp] == ?\s 173: end 174: res 175: end