RedCloth 4.1.0-universal-java
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of RedCloth might be problematic. Click here for more details.
- data/CHANGELOG +103 -0
- data/COPYING +18 -0
- data/Manifest +57 -0
- data/README +156 -0
- data/Rakefile +205 -0
- data/RedCloth.gemspec +141 -0
- data/bin/redcloth +28 -0
- data/ext/mingw-rbconfig.rb +176 -0
- data/ext/redcloth_scan/extconf.rb +9 -0
- data/ext/redcloth_scan/redcloth.h +164 -0
- data/ext/redcloth_scan/redcloth_attributes.c.rl +56 -0
- data/ext/redcloth_scan/redcloth_attributes.java.rl +96 -0
- data/ext/redcloth_scan/redcloth_attributes.rl +33 -0
- data/ext/redcloth_scan/redcloth_common.c.rl +18 -0
- data/ext/redcloth_scan/redcloth_common.java.rl +18 -0
- data/ext/redcloth_scan/redcloth_common.rl +111 -0
- data/ext/redcloth_scan/redcloth_inline.c.rl +159 -0
- data/ext/redcloth_scan/redcloth_inline.java.rl +108 -0
- data/ext/redcloth_scan/redcloth_inline.rl +157 -0
- data/ext/redcloth_scan/redcloth_scan.c.rl +227 -0
- data/ext/redcloth_scan/redcloth_scan.java.rl +555 -0
- data/ext/redcloth_scan/redcloth_scan.rl +323 -0
- data/extras/ragel_profiler.rb +73 -0
- data/lib/case_sensitive_require/RedCloth.rb +6 -0
- data/lib/redcloth.rb +37 -0
- data/lib/redcloth/erb_extension.rb +27 -0
- data/lib/redcloth/formatters/base.rb +57 -0
- data/lib/redcloth/formatters/html.rb +349 -0
- data/lib/redcloth/formatters/latex.rb +249 -0
- data/lib/redcloth/formatters/latex_entities.yml +2414 -0
- data/lib/redcloth/textile_doc.rb +105 -0
- data/lib/redcloth/version.rb +28 -0
- data/lib/redcloth_scan.jar +0 -0
- data/setup.rb +1585 -0
- data/test/basic.yml +870 -0
- data/test/code.yml +229 -0
- data/test/definitions.yml +82 -0
- data/test/extra_whitespace.yml +64 -0
- data/test/filter_html.yml +177 -0
- data/test/filter_pba.yml +20 -0
- data/test/helper.rb +108 -0
- data/test/html.yml +305 -0
- data/test/images.yml +246 -0
- data/test/instiki.yml +38 -0
- data/test/links.yml +259 -0
- data/test/lists.yml +283 -0
- data/test/poignant.yml +89 -0
- data/test/sanitize_html.yml +42 -0
- data/test/table.yml +267 -0
- data/test/test_custom_tags.rb +46 -0
- data/test/test_erb.rb +13 -0
- data/test/test_extensions.rb +31 -0
- data/test/test_formatters.rb +24 -0
- data/test/test_parser.rb +73 -0
- data/test/test_restrictions.rb +41 -0
- data/test/textism.yml +480 -0
- data/test/threshold.yml +772 -0
- data/test/validate_fixtures.rb +73 -0
- metadata +139 -0
@@ -0,0 +1,57 @@
|
|
1
|
+
module RedCloth::Formatters
|
2
|
+
module Base
|
3
|
+
|
4
|
+
def pba(opts)
|
5
|
+
opts.delete(:style) if filter_styles
|
6
|
+
opts.delete(:class) if filter_classes
|
7
|
+
opts.delete(:id) if filter_ids
|
8
|
+
|
9
|
+
atts = ''
|
10
|
+
opts[:"text-align"] = opts.delete(:align)
|
11
|
+
opts[:style] += ';' if opts[:style] && (opts[:style][-1..-1] != ';')
|
12
|
+
[:float, :"text-align", :"vertical-align"].each do |a|
|
13
|
+
opts[:style] = "#{a}:#{opts[a]};#{opts[:style]}" if opts[a]
|
14
|
+
end
|
15
|
+
[:"padding-right", :"padding-left"].each do |a|
|
16
|
+
opts[:style] = "#{a}:#{opts[a]}em;#{opts[:style]}" if opts[a]
|
17
|
+
end
|
18
|
+
[:style, :class, :lang, :id, :colspan, :rowspan, :title, :start, :align].each do |a|
|
19
|
+
atts << " #{a}=\"#{ html_esc(opts[a].to_s, :html_escape_attributes) }\"" if opts[a]
|
20
|
+
end
|
21
|
+
atts
|
22
|
+
end
|
23
|
+
|
24
|
+
def ignore(opts)
|
25
|
+
opts[:text]
|
26
|
+
end
|
27
|
+
alias_method :notextile, :ignore
|
28
|
+
|
29
|
+
def redcloth_version(opts)
|
30
|
+
p(:text => "#{opts[:prefix]}#{RedCloth::VERSION}")
|
31
|
+
end
|
32
|
+
|
33
|
+
def inline_redcloth_version(opts)
|
34
|
+
RedCloth::VERSION::STRING
|
35
|
+
end
|
36
|
+
|
37
|
+
[:del_phrase, :sup_phrase, :sub_phrase, :span_phrase].each do |phrase_method|
|
38
|
+
method = phrase_method.to_s.split('_')[0]
|
39
|
+
define_method(phrase_method) do |opts|
|
40
|
+
"#{opts[:beginning_space]}#{self.send(method, opts)}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def method_missing(method, opts)
|
45
|
+
opts[:text] || ""
|
46
|
+
end
|
47
|
+
|
48
|
+
def before_transform(text)
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
def after_transform(text)
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,349 @@
|
|
1
|
+
module RedCloth::Formatters::HTML
|
2
|
+
include RedCloth::Formatters::Base
|
3
|
+
|
4
|
+
# escapement for regular HTML (not in PRE tag)
|
5
|
+
def escape(text)
|
6
|
+
html_esc(text)
|
7
|
+
end
|
8
|
+
|
9
|
+
# escapement for HTML in a PRE tag
|
10
|
+
def escape_pre(text)
|
11
|
+
html_esc(text, :html_escape_preformatted)
|
12
|
+
end
|
13
|
+
|
14
|
+
# escaping for HTML attributes
|
15
|
+
def escape_attribute(text)
|
16
|
+
html_esc(text, :html_escape_attributes)
|
17
|
+
end
|
18
|
+
|
19
|
+
def after_transform(text)
|
20
|
+
text.chomp!
|
21
|
+
end
|
22
|
+
|
23
|
+
[:h1, :h2, :h3, :h4, :h5, :h6, :p, :pre, :div].each do |m|
|
24
|
+
define_method(m) do |opts|
|
25
|
+
"<#{m}#{pba(opts)}>#{opts[:text]}</#{m}>\n"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
[:strong, :code, :em, :i, :b, :ins, :sup, :sub, :span, :cite].each do |m|
|
30
|
+
define_method(m) do |opts|
|
31
|
+
opts[:block] = true
|
32
|
+
"<#{m}#{pba(opts)}>#{opts[:text]}</#{m}>"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def acronym(opts)
|
37
|
+
opts[:block] = true
|
38
|
+
"<acronym#{pba(opts)}>#{caps(:text => opts[:text])}</acronym>"
|
39
|
+
end
|
40
|
+
|
41
|
+
def caps(opts)
|
42
|
+
if no_span_caps
|
43
|
+
opts[:text]
|
44
|
+
else
|
45
|
+
opts[:class] = 'caps'
|
46
|
+
span(opts)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def del(opts)
|
51
|
+
opts[:block] = true
|
52
|
+
"<del#{pba(opts)}>#{opts[:text]}</del>"
|
53
|
+
end
|
54
|
+
|
55
|
+
[:ol, :ul].each do |m|
|
56
|
+
define_method("#{m}_open") do |opts|
|
57
|
+
opts[:block] = true
|
58
|
+
"#{"\n" if opts[:nest] > 1}#{"\t" * (opts[:nest] - 1)}<#{m}#{pba(opts)}>\n"
|
59
|
+
end
|
60
|
+
define_method("#{m}_close") do |opts|
|
61
|
+
"#{li_close}#{"\t" * (opts[:nest] - 1)}</#{m}>#{"\n" if opts[:nest] <= 1}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def li_open(opts)
|
66
|
+
"#{li_close unless opts.delete(:first)}#{"\t" * opts[:nest]}<li#{pba(opts)}>#{opts[:text]}"
|
67
|
+
end
|
68
|
+
|
69
|
+
def li_close(opts=nil)
|
70
|
+
"</li>\n"
|
71
|
+
end
|
72
|
+
|
73
|
+
def dl_open(opts)
|
74
|
+
opts[:block] = true
|
75
|
+
"<dl#{pba(opts)}>\n"
|
76
|
+
end
|
77
|
+
|
78
|
+
def dl_close(opts=nil)
|
79
|
+
"</dl>\n"
|
80
|
+
end
|
81
|
+
|
82
|
+
[:dt, :dd].each do |m|
|
83
|
+
define_method(m) do |opts|
|
84
|
+
"\t<#{m}#{pba(opts)}>#{opts[:text]}</#{m}>\n"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def td(opts)
|
89
|
+
tdtype = opts[:th] ? 'th' : 'td'
|
90
|
+
"\t\t<#{tdtype}#{pba(opts)}>#{opts[:text]}</#{tdtype}>\n"
|
91
|
+
end
|
92
|
+
|
93
|
+
def tr_open(opts)
|
94
|
+
"\t<tr#{pba(opts)}>\n"
|
95
|
+
end
|
96
|
+
|
97
|
+
def tr_close(opts)
|
98
|
+
"\t</tr>\n"
|
99
|
+
end
|
100
|
+
|
101
|
+
def table_open(opts)
|
102
|
+
"<table#{pba(opts)}>\n"
|
103
|
+
end
|
104
|
+
|
105
|
+
def table_close(opts)
|
106
|
+
"</table>\n"
|
107
|
+
end
|
108
|
+
|
109
|
+
def bc_open(opts)
|
110
|
+
opts[:block] = true
|
111
|
+
"<pre#{pba(opts)}>"
|
112
|
+
end
|
113
|
+
|
114
|
+
def bc_close(opts)
|
115
|
+
"</pre>\n"
|
116
|
+
end
|
117
|
+
|
118
|
+
def bq_open(opts)
|
119
|
+
opts[:block] = true
|
120
|
+
cite = opts[:cite] ? " cite=\"#{ escape_attribute opts[:cite] }\"" : ''
|
121
|
+
"<blockquote#{cite}#{pba(opts)}>\n"
|
122
|
+
end
|
123
|
+
|
124
|
+
def bq_close(opts)
|
125
|
+
"</blockquote>\n"
|
126
|
+
end
|
127
|
+
|
128
|
+
LINK_TEXT_WITH_TITLE_RE = /
|
129
|
+
([^"]+?) # $text
|
130
|
+
\s?
|
131
|
+
\(([^)]+?)\) # $title
|
132
|
+
$
|
133
|
+
/x
|
134
|
+
def link(opts)
|
135
|
+
if opts[:name] =~ LINK_TEXT_WITH_TITLE_RE
|
136
|
+
md = LINK_TEXT_WITH_TITLE_RE.match(opts[:name])
|
137
|
+
opts[:name] = md[1]
|
138
|
+
opts[:title] = md[2]
|
139
|
+
end
|
140
|
+
"<a href=\"#{escape_attribute opts[:href]}\"#{pba(opts)}>#{opts[:name]}</a>"
|
141
|
+
end
|
142
|
+
|
143
|
+
def image(opts)
|
144
|
+
opts.delete(:align)
|
145
|
+
opts[:alt] = opts[:title]
|
146
|
+
img = "<img src=\"#{escape_attribute opts[:src]}\"#{pba(opts)} alt=\"#{escape_attribute opts[:alt].to_s}\" />"
|
147
|
+
img = "<a href=\"#{escape_attribute opts[:href]}\">#{img}</a>" if opts[:href]
|
148
|
+
img
|
149
|
+
end
|
150
|
+
|
151
|
+
def footno(opts)
|
152
|
+
opts[:id] ||= opts[:text]
|
153
|
+
%Q{<sup class="footnote"><a href=\"#fn#{opts[:id]}\">#{opts[:text]}</a></sup>}
|
154
|
+
end
|
155
|
+
|
156
|
+
def fn(opts)
|
157
|
+
no = opts[:id]
|
158
|
+
opts[:id] = "fn#{no}"
|
159
|
+
opts[:class] = ["footnote", opts[:class]].compact.join(" ")
|
160
|
+
"<p#{pba(opts)}><sup>#{no}</sup> #{opts[:text]}</p>\n"
|
161
|
+
end
|
162
|
+
|
163
|
+
def snip(opts)
|
164
|
+
"<pre#{pba(opts)}><code>#{opts[:text]}</code></pre>\n"
|
165
|
+
end
|
166
|
+
|
167
|
+
def quote1(opts)
|
168
|
+
"‘#{opts[:text]}’"
|
169
|
+
end
|
170
|
+
|
171
|
+
def quote2(opts)
|
172
|
+
"“#{opts[:text]}”"
|
173
|
+
end
|
174
|
+
|
175
|
+
def multi_paragraph_quote(opts)
|
176
|
+
"“#{opts[:text]}"
|
177
|
+
end
|
178
|
+
|
179
|
+
def ellipsis(opts)
|
180
|
+
"#{opts[:text]}…"
|
181
|
+
end
|
182
|
+
|
183
|
+
def emdash(opts)
|
184
|
+
"—"
|
185
|
+
end
|
186
|
+
|
187
|
+
def endash(opts)
|
188
|
+
" – "
|
189
|
+
end
|
190
|
+
|
191
|
+
def arrow(opts)
|
192
|
+
"→"
|
193
|
+
end
|
194
|
+
|
195
|
+
def dim(opts)
|
196
|
+
opts[:text].gsub!('x', '×')
|
197
|
+
opts[:text].gsub!("'", '′')
|
198
|
+
opts[:text].gsub!('"', '″')
|
199
|
+
opts[:text]
|
200
|
+
end
|
201
|
+
|
202
|
+
def trademark(opts)
|
203
|
+
"™"
|
204
|
+
end
|
205
|
+
|
206
|
+
def registered(opts)
|
207
|
+
"®"
|
208
|
+
end
|
209
|
+
|
210
|
+
def copyright(opts)
|
211
|
+
"©"
|
212
|
+
end
|
213
|
+
|
214
|
+
def entity(opts)
|
215
|
+
"&#{opts[:text]};"
|
216
|
+
end
|
217
|
+
|
218
|
+
def amp(opts)
|
219
|
+
"&"
|
220
|
+
end
|
221
|
+
|
222
|
+
def gt(opts)
|
223
|
+
">"
|
224
|
+
end
|
225
|
+
|
226
|
+
def lt(opts)
|
227
|
+
"<"
|
228
|
+
end
|
229
|
+
|
230
|
+
def br(opts)
|
231
|
+
if hard_breaks == false
|
232
|
+
"\n"
|
233
|
+
else
|
234
|
+
"<br />\n"
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def quot(opts)
|
239
|
+
"""
|
240
|
+
end
|
241
|
+
|
242
|
+
def squot(opts)
|
243
|
+
"’"
|
244
|
+
end
|
245
|
+
|
246
|
+
def apos(opts)
|
247
|
+
"'"
|
248
|
+
end
|
249
|
+
|
250
|
+
def html(opts)
|
251
|
+
"#{opts[:text]}\n"
|
252
|
+
end
|
253
|
+
|
254
|
+
def html_block(opts)
|
255
|
+
inline_html(:text => "#{opts[:indent_before_start]}#{opts[:start_tag]}#{opts[:indent_after_start]}") +
|
256
|
+
"#{opts[:text]}" +
|
257
|
+
inline_html(:text => "#{opts[:indent_before_end]}#{opts[:end_tag]}#{opts[:indent_after_end]}")
|
258
|
+
end
|
259
|
+
|
260
|
+
def notextile(opts)
|
261
|
+
if filter_html
|
262
|
+
html_esc(opts[:text], :html_escape_preformatted)
|
263
|
+
else
|
264
|
+
opts[:text]
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def inline_html(opts)
|
269
|
+
if filter_html
|
270
|
+
html_esc(opts[:text], :html_escape_preformatted)
|
271
|
+
else
|
272
|
+
"#{opts[:text]}" # nil-safe
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def ignored_line(opts)
|
277
|
+
opts[:text] + "\n"
|
278
|
+
end
|
279
|
+
|
280
|
+
def before_transform(text)
|
281
|
+
clean_html(text) if sanitize_html
|
282
|
+
end
|
283
|
+
|
284
|
+
# HTML cleansing stuff
|
285
|
+
BASIC_TAGS = {
|
286
|
+
'a' => ['href', 'title'],
|
287
|
+
'img' => ['src', 'alt', 'title'],
|
288
|
+
'br' => [],
|
289
|
+
'i' => nil,
|
290
|
+
'u' => nil,
|
291
|
+
'b' => nil,
|
292
|
+
'pre' => nil,
|
293
|
+
'kbd' => nil,
|
294
|
+
'code' => ['lang'],
|
295
|
+
'cite' => nil,
|
296
|
+
'strong' => nil,
|
297
|
+
'em' => nil,
|
298
|
+
'ins' => nil,
|
299
|
+
'sup' => nil,
|
300
|
+
'sub' => nil,
|
301
|
+
'del' => nil,
|
302
|
+
'table' => nil,
|
303
|
+
'tr' => nil,
|
304
|
+
'td' => ['colspan', 'rowspan'],
|
305
|
+
'th' => nil,
|
306
|
+
'ol' => ['start'],
|
307
|
+
'ul' => nil,
|
308
|
+
'li' => nil,
|
309
|
+
'p' => nil,
|
310
|
+
'h1' => nil,
|
311
|
+
'h2' => nil,
|
312
|
+
'h3' => nil,
|
313
|
+
'h4' => nil,
|
314
|
+
'h5' => nil,
|
315
|
+
'h6' => nil,
|
316
|
+
'blockquote' => ['cite'],
|
317
|
+
'notextile' => nil
|
318
|
+
}
|
319
|
+
|
320
|
+
# Clean unauthorized tags.
|
321
|
+
def clean_html( text, allowed_tags = BASIC_TAGS )
|
322
|
+
text.gsub!( /<!\[CDATA\[/, '' )
|
323
|
+
text.gsub!( /<(\/*)([A-Za-z]\w*)([^>]*?)(\s?\/?)>/ ) do |m|
|
324
|
+
raw = $~
|
325
|
+
tag = raw[2].downcase
|
326
|
+
if allowed_tags.has_key? tag
|
327
|
+
pcs = [tag]
|
328
|
+
allowed_tags[tag].each do |prop|
|
329
|
+
['"', "'", ''].each do |q|
|
330
|
+
q2 = ( q != '' ? q : '\s' )
|
331
|
+
if raw[3] =~ /#{prop}\s*=\s*#{q}([^#{q2}]+)#{q}/i
|
332
|
+
attrv = $1
|
333
|
+
next if (prop == 'src' or prop == 'href') and not attrv =~ %r{^(http|https|ftp):}
|
334
|
+
pcs << "#{prop}=\"#{attrv.gsub('"', '\\"')}\""
|
335
|
+
break
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end if allowed_tags[tag]
|
339
|
+
"<#{raw[1]}#{pcs.join " "}#{raw[4]}>"
|
340
|
+
else # Unauthorized tag
|
341
|
+
if block_given?
|
342
|
+
yield m
|
343
|
+
else
|
344
|
+
''
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
@@ -0,0 +1,249 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module RedCloth::Formatters::LATEX
|
4
|
+
include RedCloth::Formatters::Base
|
5
|
+
|
6
|
+
ENTITIES = YAML::load(File.read(File.dirname(__FILE__)+'/latex_entities.yml'))
|
7
|
+
|
8
|
+
module Settings
|
9
|
+
# Maps CSS style names to latex formatting options
|
10
|
+
def latex_image_styles
|
11
|
+
@latex_image_class_styles ||= {}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
RedCloth::TextileDoc.send(:include, Settings)
|
16
|
+
|
17
|
+
def escape(text)
|
18
|
+
latex_esc(text)
|
19
|
+
end
|
20
|
+
|
21
|
+
def escape_pre(text)
|
22
|
+
text
|
23
|
+
end
|
24
|
+
|
25
|
+
# headers
|
26
|
+
{ :h1 => 'section*',
|
27
|
+
:h2 => 'subsection*',
|
28
|
+
:h3 => 'subsubsection*',
|
29
|
+
:h4 => 'textbf',
|
30
|
+
:h5 => 'textbf',
|
31
|
+
:h6 => 'textbf',
|
32
|
+
}.each do |m,tag|
|
33
|
+
define_method(m) do |opts|
|
34
|
+
"\\#{tag}{#{opts[:text]}}\n\n"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# commands
|
39
|
+
{ :strong => 'textbf',
|
40
|
+
:em => 'emph',
|
41
|
+
:i => 'emph',
|
42
|
+
:b => 'textbf',
|
43
|
+
:ins => 'underline',
|
44
|
+
:del => 'sout',
|
45
|
+
:acronym => 'MakeUppercase',
|
46
|
+
:caps => 'MakeUppercase',
|
47
|
+
}.each do |m,tag|
|
48
|
+
define_method(m) do |opts|
|
49
|
+
"\\#{tag}{#{opts[:text]}}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
{ :sup => '\ensuremath{^\textrm{#1}}',
|
54
|
+
:sub => '\ensuremath{_\textrm{#1}}',
|
55
|
+
}.each do |m, expr|
|
56
|
+
define_method(m) do |opts|
|
57
|
+
expr.sub('#1', opts[:text])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# environments
|
62
|
+
{ :pre => 'verbatim',
|
63
|
+
:code => 'verbatim',
|
64
|
+
:cite => 'quote',
|
65
|
+
}.each do |m, env|
|
66
|
+
define_method(m) do |opts|
|
67
|
+
begin_chunk(env) + opts[:text] + end_chunk(env)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# ignore (or find a good solution later)
|
72
|
+
[ :span,
|
73
|
+
:div,
|
74
|
+
].each do |m|
|
75
|
+
define_method(m) do |opts|
|
76
|
+
opts[:text].to_s
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
{ :ol => 'enumerate',
|
81
|
+
:ul => 'itemize',
|
82
|
+
}.each do |m, env|
|
83
|
+
define_method("#{m}_open") do |opts|
|
84
|
+
opts[:block] = true
|
85
|
+
"\\begin{#{env}}\n"
|
86
|
+
end
|
87
|
+
define_method("#{m}_close") do |opts|
|
88
|
+
"#{li_close}\\end{#{env}}\n\n"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def li_open(opts)
|
93
|
+
"#{li_close unless opts.delete(:first)}\t\\item #{opts[:text]}"
|
94
|
+
end
|
95
|
+
|
96
|
+
def li_close(opts=nil)
|
97
|
+
"\n"
|
98
|
+
end
|
99
|
+
|
100
|
+
def p(opts)
|
101
|
+
opts[:text] + "\n\n"
|
102
|
+
end
|
103
|
+
|
104
|
+
def td(opts)
|
105
|
+
"\t\t\t#{opts[:text]} &\n"
|
106
|
+
end
|
107
|
+
|
108
|
+
def tr_open(opts)
|
109
|
+
"\t\t"
|
110
|
+
end
|
111
|
+
|
112
|
+
def tr_close(opts)
|
113
|
+
"\t\t\\\\\n"
|
114
|
+
end
|
115
|
+
|
116
|
+
# FIXME: we need to know the column count before opening tabular context.
|
117
|
+
def table_open(opts)
|
118
|
+
"\\begin{align*}\n"
|
119
|
+
end
|
120
|
+
|
121
|
+
def table_close(opts)
|
122
|
+
"\t\\end{align*}\n"
|
123
|
+
end
|
124
|
+
|
125
|
+
def bc_open(opts)
|
126
|
+
opts[:block] = true
|
127
|
+
begin_chunk("verbatim") + "\n"
|
128
|
+
end
|
129
|
+
|
130
|
+
def bc_close(opts)
|
131
|
+
end_chunk("verbatim") + "\n"
|
132
|
+
end
|
133
|
+
|
134
|
+
def bq_open(opts)
|
135
|
+
opts[:block] = true
|
136
|
+
"\\begin{quotation}\n"
|
137
|
+
end
|
138
|
+
|
139
|
+
def bq_close(opts)
|
140
|
+
"\\end{quotation}\n\n"
|
141
|
+
end
|
142
|
+
|
143
|
+
def link(opts)
|
144
|
+
"\\href{#{opts[:href]}}{#{opts[:name]}}"
|
145
|
+
end
|
146
|
+
|
147
|
+
# FIXME: use includegraphics with security verification
|
148
|
+
#
|
149
|
+
# Remember to use '\RequirePackage{graphicx}' in your LaTeX header
|
150
|
+
#
|
151
|
+
# FIXME: Look at dealing with width / height gracefully as this should be
|
152
|
+
# specified in a unit like cm rather than px.
|
153
|
+
def image(opts)
|
154
|
+
# Don't know how to use remote links, plus can we trust them?
|
155
|
+
return "" if opts[:src] =~ /^\w+\:\/\//
|
156
|
+
# Resolve CSS styles if any have been set
|
157
|
+
styling = opts[:class].to_s.split(/\s+/).collect { |style| latex_image_styles[style] }.compact.join ','
|
158
|
+
# Build latex code
|
159
|
+
[ "\\begin{figure}[htp]",
|
160
|
+
" \\includegraphics[#{styling}]{#{opts[:src]}}",
|
161
|
+
(" \\caption{#{escape opts[:title]}}" if opts[:title]),
|
162
|
+
(" \\label{#{escape opts[:alt]}}" if opts[:alt]),
|
163
|
+
"\\end{figure}",
|
164
|
+
].compact.join "\n"
|
165
|
+
end
|
166
|
+
|
167
|
+
def footno(opts)
|
168
|
+
# TODO: insert a placeholder until we know the footnote content.
|
169
|
+
# For this to work, we need some kind of post-processing...
|
170
|
+
"\\footnotemark[#{opts[:text]}]"
|
171
|
+
end
|
172
|
+
|
173
|
+
def fn(opts)
|
174
|
+
"\\footnotetext[#{opts[:id]}]{#{opts[:text]}}"
|
175
|
+
end
|
176
|
+
|
177
|
+
def snip(opts)
|
178
|
+
"\\begin{verbatim}#{opts[:text]}\\end{verbatim}"
|
179
|
+
end
|
180
|
+
|
181
|
+
def quote1(opts)
|
182
|
+
"`#{opts[:text]}'"
|
183
|
+
end
|
184
|
+
|
185
|
+
def quote2(opts)
|
186
|
+
"``#{opts[:text]}\""
|
187
|
+
end
|
188
|
+
|
189
|
+
def ellipsis(opts)
|
190
|
+
"#{opts[:text]}\\ldots{}"
|
191
|
+
end
|
192
|
+
|
193
|
+
def emdash(opts)
|
194
|
+
"---"
|
195
|
+
end
|
196
|
+
|
197
|
+
def endash(opts)
|
198
|
+
"--"
|
199
|
+
end
|
200
|
+
|
201
|
+
def arrow(opts)
|
202
|
+
"\\rightarrow{}"
|
203
|
+
end
|
204
|
+
|
205
|
+
def trademark(opts)
|
206
|
+
"\\texttrademark{}"
|
207
|
+
end
|
208
|
+
|
209
|
+
def registered(opts)
|
210
|
+
"\\textregistered{}"
|
211
|
+
end
|
212
|
+
|
213
|
+
def copyright(opts)
|
214
|
+
"\\copyright{}"
|
215
|
+
end
|
216
|
+
|
217
|
+
# TODO: what do we do with (unknown) unicode entities ?
|
218
|
+
#
|
219
|
+
def entity(opts)
|
220
|
+
text = opts[:text][0..0] == '#' ? opts[:text][1..-1] : opts[:text]
|
221
|
+
ENTITIES[text]
|
222
|
+
end
|
223
|
+
|
224
|
+
def dim(opts)
|
225
|
+
space = opts[:space] ? " " : ''
|
226
|
+
"#{opts[:text]}#{space}\\texttimes{}#{space}"
|
227
|
+
end
|
228
|
+
|
229
|
+
private
|
230
|
+
|
231
|
+
# Use this for block level commands that use \begin
|
232
|
+
def begin_chunk(type)
|
233
|
+
chunk_counter[type] += 1
|
234
|
+
return "\\begin{#{type}}" if 1 == chunk_counter[type]
|
235
|
+
''
|
236
|
+
end
|
237
|
+
|
238
|
+
# Use this for block level commands that use \end
|
239
|
+
def end_chunk(type)
|
240
|
+
chunk_counter[type] -= 1
|
241
|
+
raise RuntimeError, "Bad latex #{type} nesting detected" if chunk_counter[type] < 0 # This should never need to happen
|
242
|
+
return "\\end{#{type}}" if 0 == chunk_counter[type]
|
243
|
+
''
|
244
|
+
end
|
245
|
+
|
246
|
+
def chunk_counter
|
247
|
+
@chunk_counter ||= Hash.new 0
|
248
|
+
end
|
249
|
+
end
|