mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Renamespace lib/rdoc/markup from SM::SimpleMarkup to RDoc::Markup.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@15033 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
cbd4604c53
commit
fcb0b1f503
31 changed files with 2573 additions and 2863 deletions
331
lib/rdoc/markup/to_latex.rb
Normal file
331
lib/rdoc/markup/to_latex.rb
Normal file
|
@ -0,0 +1,331 @@
|
|||
require 'rdoc/markup/fragments'
|
||||
require 'rdoc/markup/inline'
|
||||
|
||||
require 'cgi'
|
||||
|
||||
##
|
||||
# Convert SimpleMarkup to basic LaTeX report format.
|
||||
|
||||
class RDoc::Markup::ToLaTeX
|
||||
|
||||
BS = "\020" # \
|
||||
OB = "\021" # {
|
||||
CB = "\022" # }
|
||||
DL = "\023" # Dollar
|
||||
|
||||
BACKSLASH = "#{BS}symbol#{OB}92#{CB}"
|
||||
HAT = "#{BS}symbol#{OB}94#{CB}"
|
||||
BACKQUOTE = "#{BS}symbol#{OB}0#{CB}"
|
||||
TILDE = "#{DL}#{BS}sim#{DL}"
|
||||
LESSTHAN = "#{DL}<#{DL}"
|
||||
GREATERTHAN = "#{DL}>#{DL}"
|
||||
|
||||
def self.l(str)
|
||||
str.tr('\\', BS).tr('{', OB).tr('}', CB).tr('$', DL)
|
||||
end
|
||||
|
||||
def l(arg)
|
||||
RDoc::Markup::ToLaTeX.l(arg)
|
||||
end
|
||||
|
||||
LIST_TYPE_TO_LATEX = {
|
||||
ListBase::BULLET => [ l("\\begin{itemize}"), l("\\end{itemize}") ],
|
||||
ListBase::NUMBER => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\arabic" ],
|
||||
ListBase::UPPERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\Alph" ],
|
||||
ListBase::LOWERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\alph" ],
|
||||
ListBase::LABELED => [ l("\\begin{description}"), l("\\end{description}") ],
|
||||
ListBase::NOTE => [
|
||||
l("\\begin{tabularx}{\\linewidth}{@{} l X @{}}"),
|
||||
l("\\end{tabularx}") ],
|
||||
}
|
||||
|
||||
InlineTag = Struct.new(:bit, :on, :off)
|
||||
|
||||
def initialize
|
||||
init_tags
|
||||
@list_depth = 0
|
||||
@prev_list_types = []
|
||||
end
|
||||
|
||||
##
|
||||
# Set up the standard mapping of attributes to LaTeX
|
||||
|
||||
def init_tags
|
||||
@attr_tags = [
|
||||
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:BOLD), l("\\textbf{"), l("}")),
|
||||
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:TT), l("\\texttt{"), l("}")),
|
||||
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:EM), l("\\emph{"), l("}")),
|
||||
]
|
||||
end
|
||||
|
||||
##
|
||||
# Escape a LaTeX string
|
||||
|
||||
def escape(str)
|
||||
$stderr.print "FE: ", str
|
||||
s = str.
|
||||
sub(/\s+$/, '').
|
||||
gsub(/([_\${}&%#])/, "#{BS}\\1").
|
||||
gsub(/\\/, BACKSLASH).
|
||||
gsub(/\^/, HAT).
|
||||
gsub(/~/, TILDE).
|
||||
gsub(/</, LESSTHAN).
|
||||
gsub(/>/, GREATERTHAN).
|
||||
gsub(/,,/, ",{},").
|
||||
gsub(/\`/, BACKQUOTE)
|
||||
$stderr.print "-> ", s, "\n"
|
||||
s
|
||||
end
|
||||
|
||||
##
|
||||
# Add a new set of LaTeX tags for an attribute. We allow
|
||||
# separate start and end tags for flexibility
|
||||
|
||||
def add_tag(name, start, stop)
|
||||
@attr_tags << InlineTag.new(RDoc::Markup::Attribute.bitmap_for(name), start, stop)
|
||||
end
|
||||
|
||||
##
|
||||
# Here's the client side of the visitor pattern
|
||||
|
||||
def start_accepting
|
||||
@res = ""
|
||||
@in_list_entry = []
|
||||
end
|
||||
|
||||
def end_accepting
|
||||
@res.tr(BS, '\\').tr(OB, '{').tr(CB, '}').tr(DL, '$')
|
||||
end
|
||||
|
||||
def accept_paragraph(am, fragment)
|
||||
@res << wrap(convert_flow(am.flow(fragment.txt)))
|
||||
@res << "\n"
|
||||
end
|
||||
|
||||
def accept_verbatim(am, fragment)
|
||||
@res << "\n\\begin{code}\n"
|
||||
@res << fragment.txt.sub(/[\n\s]+\Z/, '')
|
||||
@res << "\n\\end{code}\n\n"
|
||||
end
|
||||
|
||||
def accept_rule(am, fragment)
|
||||
size = fragment.param
|
||||
size = 10 if size > 10
|
||||
@res << "\n\n\\rule{\\linewidth}{#{size}pt}\n\n"
|
||||
end
|
||||
|
||||
def accept_list_start(am, fragment)
|
||||
@res << list_name(fragment.type, true) << "\n"
|
||||
@in_list_entry.push false
|
||||
end
|
||||
|
||||
def accept_list_end(am, fragment)
|
||||
if tag = @in_list_entry.pop
|
||||
@res << tag << "\n"
|
||||
end
|
||||
@res << list_name(fragment.type, false) << "\n"
|
||||
end
|
||||
|
||||
def accept_list_item(am, fragment)
|
||||
if tag = @in_list_entry.last
|
||||
@res << tag << "\n"
|
||||
end
|
||||
@res << list_item_start(am, fragment)
|
||||
@res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
|
||||
@in_list_entry[-1] = list_end_for(fragment.type)
|
||||
end
|
||||
|
||||
def accept_blank_line(am, fragment)
|
||||
# @res << "\n"
|
||||
end
|
||||
|
||||
def accept_heading(am, fragment)
|
||||
@res << convert_heading(fragment.head_level, am.flow(fragment.txt))
|
||||
end
|
||||
|
||||
##
|
||||
# This is a higher speed (if messier) version of wrap
|
||||
|
||||
def wrap(txt, line_len = 76)
|
||||
res = ""
|
||||
sp = 0
|
||||
ep = txt.length
|
||||
while sp < ep
|
||||
# scan back for a space
|
||||
p = sp + line_len - 1
|
||||
if p >= ep
|
||||
p = ep
|
||||
else
|
||||
while p > sp and txt[p] != ?\s
|
||||
p -= 1
|
||||
end
|
||||
if p <= sp
|
||||
p = sp + line_len
|
||||
while p < ep and txt[p] != ?\s
|
||||
p += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
res << txt[sp...p] << "\n"
|
||||
sp = p
|
||||
sp += 1 while sp < ep and txt[sp] == ?\s
|
||||
end
|
||||
res
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def on_tags(res, item)
|
||||
attr_mask = item.turn_on
|
||||
return if attr_mask.zero?
|
||||
|
||||
@attr_tags.each do |tag|
|
||||
if attr_mask & tag.bit != 0
|
||||
res << tag.on
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def off_tags(res, item)
|
||||
attr_mask = item.turn_off
|
||||
return if attr_mask.zero?
|
||||
|
||||
@attr_tags.reverse_each do |tag|
|
||||
if attr_mask & tag.bit != 0
|
||||
res << tag.off
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def convert_flow(flow)
|
||||
res = ""
|
||||
flow.each do |item|
|
||||
case item
|
||||
when String
|
||||
$stderr.puts "Converting '#{item}'"
|
||||
res << convert_string(item)
|
||||
when AttrChanger
|
||||
off_tags(res, item)
|
||||
on_tags(res, item)
|
||||
when Special
|
||||
res << convert_special(item)
|
||||
else
|
||||
raise "Unknown flow element: #{item.inspect}"
|
||||
end
|
||||
end
|
||||
res
|
||||
end
|
||||
|
||||
##
|
||||
# some of these patterns are taken from SmartyPants...
|
||||
|
||||
def convert_string(item)
|
||||
escape(item).
|
||||
|
||||
# convert ... to elipsis (and make sure .... becomes .<elipsis>)
|
||||
gsub(/\.\.\.\./, '.\ldots{}').gsub(/\.\.\./, '\ldots{}').
|
||||
|
||||
# convert single closing quote
|
||||
gsub(%r{([^ \t\r\n\[\{\(])\'}) { "#$1'" }.
|
||||
gsub(%r{\'(?=\W|s\b)}) { "'" }.
|
||||
|
||||
# convert single opening quote
|
||||
gsub(/'/, '`').
|
||||
|
||||
# convert double closing quote
|
||||
gsub(%r{([^ \t\r\n\[\{\(])\"(?=\W)}) { "#$1''" }.
|
||||
|
||||
# convert double opening quote
|
||||
gsub(/"/, "``").
|
||||
|
||||
# convert copyright
|
||||
gsub(/\(c\)/, '\copyright{}')
|
||||
|
||||
end
|
||||
|
||||
def convert_special(special)
|
||||
handled = false
|
||||
Attribute.each_name_of(special.type) do |name|
|
||||
method_name = "handle_special_#{name}"
|
||||
if self.respond_to? method_name
|
||||
special.text = send(method_name, special)
|
||||
handled = true
|
||||
end
|
||||
end
|
||||
raise "Unhandled special: #{special}" unless handled
|
||||
special.text
|
||||
end
|
||||
|
||||
def convert_heading(level, flow)
|
||||
res =
|
||||
case level
|
||||
when 1 then "\\chapter{"
|
||||
when 2 then "\\section{"
|
||||
when 3 then "\\subsection{"
|
||||
when 4 then "\\subsubsection{"
|
||||
else "\\paragraph{"
|
||||
end +
|
||||
convert_flow(flow) +
|
||||
"}\n"
|
||||
end
|
||||
|
||||
def list_name(list_type, is_open_tag)
|
||||
tags = LIST_TYPE_TO_LATEX[list_type] || raise("Invalid list type: #{list_type.inspect}")
|
||||
if tags[2] # enumerate
|
||||
if is_open_tag
|
||||
@list_depth += 1
|
||||
if @prev_list_types[@list_depth] != tags[2]
|
||||
case @list_depth
|
||||
when 1
|
||||
roman = "i"
|
||||
when 2
|
||||
roman = "ii"
|
||||
when 3
|
||||
roman = "iii"
|
||||
when 4
|
||||
roman = "iv"
|
||||
else
|
||||
raise("Too deep list: level #{@list_depth}")
|
||||
end
|
||||
@prev_list_types[@list_depth] = tags[2]
|
||||
return l("\\renewcommand{\\labelenum#{roman}}{#{tags[2]}{enum#{roman}}}") + "\n" + tags[0]
|
||||
end
|
||||
else
|
||||
@list_depth -= 1
|
||||
end
|
||||
end
|
||||
tags[ is_open_tag ? 0 : 1]
|
||||
end
|
||||
|
||||
def list_item_start(am, fragment)
|
||||
case fragment.type
|
||||
when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA,
|
||||
ListBase::LOWERALPHA
|
||||
"\\item "
|
||||
|
||||
when ListBase::LABELED
|
||||
"\\item[" + convert_flow(am.flow(fragment.param)) + "] "
|
||||
|
||||
when ListBase::NOTE
|
||||
convert_flow(am.flow(fragment.param)) + " & "
|
||||
else
|
||||
raise "Invalid list type"
|
||||
end
|
||||
end
|
||||
|
||||
def list_end_for(fragment_type)
|
||||
case fragment_type
|
||||
when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA,
|
||||
ListBase::LOWERALPHA, ListBase::LABELED
|
||||
""
|
||||
when ListBase::NOTE
|
||||
"\\\\\n"
|
||||
else
|
||||
raise "Invalid list type"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
d
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue