| 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