PageRenderTime 27ms CodeModel.GetById 0ms RepoModel.GetById 1ms app.codeStats 0ms

/benchmark/app/rdoc-2.4.3/lib/rdoc/markup/to_latex.rb

http://github.com/rubinius/rubinius
Ruby | 328 lines | 295 code | 25 blank | 8 comment | 10 complexity | d1e4a6ed3ee4eb105ac1b7e300f9e0b4 MD5 | raw file
Possible License(s): BSD-3-Clause, MPL-2.0-no-copyleft-exception, 0BSD, GPL-2.0, LGPL-2.1
  1. require 'rdoc/markup/formatter'
  2. require 'rdoc/markup/fragments'
  3. require 'rdoc/markup/inline'
  4. require 'cgi'
  5. ##
  6. # Convert SimpleMarkup to basic LaTeX report format.
  7. class RDoc::Markup::ToLaTeX < RDoc::Markup::Formatter
  8. BS = "\020" # \
  9. OB = "\021" # {
  10. CB = "\022" # }
  11. DL = "\023" # Dollar
  12. BACKSLASH = "#{BS}symbol#{OB}92#{CB}"
  13. HAT = "#{BS}symbol#{OB}94#{CB}"
  14. BACKQUOTE = "#{BS}symbol#{OB}0#{CB}"
  15. TILDE = "#{DL}#{BS}sim#{DL}"
  16. LESSTHAN = "#{DL}<#{DL}"
  17. GREATERTHAN = "#{DL}>#{DL}"
  18. def self.l(str)
  19. str.tr('\\', BS).tr('{', OB).tr('}', CB).tr('$', DL)
  20. end
  21. def l(arg)
  22. RDoc::Markup::ToLaTeX.l(arg)
  23. end
  24. LIST_TYPE_TO_LATEX = {
  25. :BULLET => [ l("\\begin{itemize}"), l("\\end{itemize}") ],
  26. :NUMBER => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\arabic" ],
  27. :UPPERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\Alph" ],
  28. :LOWERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\alph" ],
  29. :LABELED => [ l("\\begin{description}"), l("\\end{description}") ],
  30. :NOTE => [
  31. l("\\begin{tabularx}{\\linewidth}{@{} l X @{}}"),
  32. l("\\end{tabularx}") ],
  33. }
  34. InlineTag = Struct.new(:bit, :on, :off)
  35. def initialize
  36. init_tags
  37. @list_depth = 0
  38. @prev_list_types = []
  39. end
  40. ##
  41. # Set up the standard mapping of attributes to LaTeX
  42. def init_tags
  43. @attr_tags = [
  44. InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:BOLD), l("\\textbf{"), l("}")),
  45. InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:TT), l("\\texttt{"), l("}")),
  46. InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:EM), l("\\emph{"), l("}")),
  47. ]
  48. end
  49. ##
  50. # Escape a LaTeX string
  51. def escape(str)
  52. $stderr.print "FE: ", str if $DEBUG_RDOC
  53. s = str.
  54. sub(/\s+$/, '').
  55. gsub(/([_\${}&%#])/, "#{BS}\\1").
  56. gsub(/\\/, BACKSLASH).
  57. gsub(/\^/, HAT).
  58. gsub(/~/, TILDE).
  59. gsub(/</, LESSTHAN).
  60. gsub(/>/, GREATERTHAN).
  61. gsub(/,,/, ",{},").
  62. gsub(/\`/, BACKQUOTE)
  63. $stderr.print "-> ", s, "\n" if $DEBUG_RDOC
  64. s
  65. end
  66. ##
  67. # Add a new set of LaTeX tags for an attribute. We allow
  68. # separate start and end tags for flexibility
  69. def add_tag(name, start, stop)
  70. @attr_tags << InlineTag.new(RDoc::Markup::Attribute.bitmap_for(name), start, stop)
  71. end
  72. ##
  73. # This is a higher speed (if messier) version of wrap
  74. def wrap(txt, line_len = 76)
  75. res = ""
  76. sp = 0
  77. ep = txt.length
  78. while sp < ep
  79. # scan back for a space
  80. p = sp + line_len - 1
  81. if p >= ep
  82. p = ep
  83. else
  84. while p > sp and txt[p] != ?\s
  85. p -= 1
  86. end
  87. if p <= sp
  88. p = sp + line_len
  89. while p < ep and txt[p] != ?\s
  90. p += 1
  91. end
  92. end
  93. end
  94. res << txt[sp...p] << "\n"
  95. sp = p
  96. sp += 1 while sp < ep and txt[sp] == ?\s
  97. end
  98. res
  99. end
  100. ##
  101. # :section: Visitor
  102. def start_accepting
  103. @res = ""
  104. @in_list_entry = []
  105. end
  106. def end_accepting
  107. @res.tr(BS, '\\').tr(OB, '{').tr(CB, '}').tr(DL, '$')
  108. end
  109. def accept_paragraph(am, fragment)
  110. @res << wrap(convert_flow(am.flow(fragment.txt)))
  111. @res << "\n"
  112. end
  113. def accept_verbatim(am, fragment)
  114. @res << "\n\\begin{code}\n"
  115. @res << fragment.txt.sub(/[\n\s]+\Z/, '')
  116. @res << "\n\\end{code}\n\n"
  117. end
  118. def accept_rule(am, fragment)
  119. size = fragment.param
  120. size = 10 if size > 10
  121. @res << "\n\n\\rule{\\linewidth}{#{size}pt}\n\n"
  122. end
  123. def accept_list_start(am, fragment)
  124. @res << list_name(fragment.type, true) << "\n"
  125. @in_list_entry.push false
  126. end
  127. def accept_list_end(am, fragment)
  128. if tag = @in_list_entry.pop
  129. @res << tag << "\n"
  130. end
  131. @res << list_name(fragment.type, false) << "\n"
  132. end
  133. def accept_list_item(am, fragment)
  134. if tag = @in_list_entry.last
  135. @res << tag << "\n"
  136. end
  137. @res << list_item_start(am, fragment)
  138. @res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
  139. @in_list_entry[-1] = list_end_for(fragment.type)
  140. end
  141. def accept_blank_line(am, fragment)
  142. # @res << "\n"
  143. end
  144. def accept_heading(am, fragment)
  145. @res << convert_heading(fragment.head_level, am.flow(fragment.txt))
  146. end
  147. private
  148. def on_tags(res, item)
  149. attr_mask = item.turn_on
  150. return if attr_mask.zero?
  151. @attr_tags.each do |tag|
  152. if attr_mask & tag.bit != 0
  153. res << tag.on
  154. end
  155. end
  156. end
  157. def off_tags(res, item)
  158. attr_mask = item.turn_off
  159. return if attr_mask.zero?
  160. @attr_tags.reverse_each do |tag|
  161. if attr_mask & tag.bit != 0
  162. res << tag.off
  163. end
  164. end
  165. end
  166. def convert_flow(flow)
  167. res = ""
  168. flow.each do |item|
  169. case item
  170. when String
  171. $stderr.puts "Converting '#{item}'" if $DEBUG_RDOC
  172. res << convert_string(item)
  173. when AttrChanger
  174. off_tags(res, item)
  175. on_tags(res, item)
  176. when Special
  177. res << convert_special(item)
  178. else
  179. raise "Unknown flow element: #{item.inspect}"
  180. end
  181. end
  182. res
  183. end
  184. ##
  185. # some of these patterns are taken from SmartyPants...
  186. def convert_string(item)
  187. escape(item).
  188. # convert ... to elipsis (and make sure .... becomes .<elipsis>)
  189. gsub(/\.\.\.\./, '.\ldots{}').gsub(/\.\.\./, '\ldots{}').
  190. # convert single closing quote
  191. gsub(%r{([^ \t\r\n\[\{\(])\'}, '\1\'').
  192. gsub(%r{\'(?=\W|s\b)}, "'" ).
  193. # convert single opening quote
  194. gsub(/'/, '`').
  195. # convert double closing quote
  196. gsub(%r{([^ \t\r\n\[\{\(])\"(?=\W)}, "\\1''").
  197. # convert double opening quote
  198. gsub(/"/, "``").
  199. # convert copyright
  200. gsub(/\(c\)/, '\copyright{}')
  201. end
  202. def convert_special(special)
  203. handled = false
  204. Attribute.each_name_of(special.type) do |name|
  205. method_name = "handle_special_#{name}"
  206. if self.respond_to? method_name
  207. special.text = send(method_name, special)
  208. handled = true
  209. end
  210. end
  211. raise "Unhandled special: #{special}" unless handled
  212. special.text
  213. end
  214. def convert_heading(level, flow)
  215. res =
  216. case level
  217. when 1 then "\\chapter{"
  218. when 2 then "\\section{"
  219. when 3 then "\\subsection{"
  220. when 4 then "\\subsubsection{"
  221. else "\\paragraph{"
  222. end +
  223. convert_flow(flow) +
  224. "}\n"
  225. end
  226. def list_name(list_type, is_open_tag)
  227. tags = LIST_TYPE_TO_LATEX[list_type] || raise("Invalid list type: #{list_type.inspect}")
  228. if tags[2] # enumerate
  229. if is_open_tag
  230. @list_depth += 1
  231. if @prev_list_types[@list_depth] != tags[2]
  232. case @list_depth
  233. when 1
  234. roman = "i"
  235. when 2
  236. roman = "ii"
  237. when 3
  238. roman = "iii"
  239. when 4
  240. roman = "iv"
  241. else
  242. raise("Too deep list: level #{@list_depth}")
  243. end
  244. @prev_list_types[@list_depth] = tags[2]
  245. return l("\\renewcommand{\\labelenum#{roman}}{#{tags[2]}{enum#{roman}}}") + "\n" + tags[0]
  246. end
  247. else
  248. @list_depth -= 1
  249. end
  250. end
  251. tags[ is_open_tag ? 0 : 1]
  252. end
  253. def list_item_start(am, fragment)
  254. case fragment.type
  255. when :BULLET, :NUMBER, :UPPERALPHA, :LOWERALPHA then
  256. "\\item "
  257. when :LABELED then
  258. "\\item[" + convert_flow(am.flow(fragment.param)) + "] "
  259. when :NOTE then
  260. convert_flow(am.flow(fragment.param)) + " & "
  261. else
  262. raise "Invalid list type"
  263. end
  264. end
  265. def list_end_for(fragment_type)
  266. case fragment_type
  267. when :BULLET, :NUMBER, :UPPERALPHA, :LOWERALPHA, :LABELED then
  268. ""
  269. when :NOTE
  270. "\\\\\n"
  271. else
  272. raise "Invalid list type"
  273. end
  274. end
  275. end