awhyte-rtex 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,86 @@
1
+ require File.dirname(__FILE__) << '/test_helper'
2
+
3
+ class DocumentTest < Test::Unit::TestCase
4
+
5
+ context "Document Generation" do
6
+
7
+ setup do
8
+ change_tmpdir_for_testing
9
+ end
10
+
11
+ should "have a to_pdf method" do
12
+ assert document(:first).respond_to?(:to_pdf)
13
+ end
14
+
15
+ context "when escaping" do
16
+ setup do
17
+ @obj = Object.new
18
+ def @obj.to_s
19
+ '\~'
20
+ end
21
+ @escaped = '\textbackslash{}\textasciitilde{}'
22
+ end
23
+ should "escape character" do
24
+ assert_equal @escaped, RTeX::Document.escape(@obj.to_s)
25
+ end
26
+ should "convert argument to string before attempting escape" do
27
+ assert_equal @escaped, RTeX::Document.escape(@obj)
28
+ end
29
+ end
30
+
31
+ should "use a to_pdf block to move a file to a relative path" do
32
+ begin
33
+ path = File.expand_path(File.dirname(__FILE__) << '/tmp/this_is_relative_to_pwd.pdf')
34
+ document(:first).to_pdf do |filename|
35
+ assert_nothing_raised do
36
+ FileUtils.move filename, path
37
+ end
38
+ assert File.exists?(path)
39
+ end
40
+ ensure
41
+ FileUtils.rm path rescue nil
42
+ end
43
+ end
44
+
45
+ should "generate PDF and return as a string" do
46
+ @author = 'Foo'
47
+ assert_equal '%PDF', document(:first).to_pdf(binding)[0, 4]
48
+ end
49
+
50
+ should "generate TeX source and return as a string with debug option" do
51
+ @author = 'Foo'
52
+ assert_not_equal '%PDF', document(:first, :tex => true).to_pdf(binding)[0, 4]
53
+ end
54
+
55
+ should "generate PDF and give access to file directly" do
56
+ @author = 'Foo'
57
+ data_read = nil
58
+ invocation_result = document(:first).to_pdf(binding) do |filename|
59
+ data_read = File.open(filename, 'rb') { |f| f.read }
60
+ :not_the_file_contents
61
+ end
62
+ assert_equal '%PDF', data_read[0, 4]
63
+ assert_equal :not_the_file_contents, invocation_result
64
+ end
65
+
66
+ should "generate TeX source and give access to file directly" do
67
+ @author = 'Foo'
68
+ data_read = nil
69
+ invocation_result = document(:first, :tex => true).to_pdf(binding) do |filename|
70
+ data_read = File.open(filename, 'rb') { |f| f.read }
71
+ :not_the_file_contents
72
+ end
73
+ assert_not_equal '%PDF', data_read[0, 4]
74
+ assert_equal :not_the_file_contents, invocation_result
75
+ end
76
+
77
+ should "wrap in a layout using `yield'" do
78
+ doc = document(:fragment, :layout => 'testing_layout[<%= yield %>]')
79
+ @name = 'ERB'
80
+ source = doc.source(binding)
81
+ assert source =~ /^testing_layout.*?ERB, Fragmented/
82
+ end
83
+
84
+ end
85
+
86
+ end
@@ -0,0 +1,24 @@
1
+ require File.dirname(__FILE__) << '/test_helper'
2
+
3
+ class FilterTest < Test::Unit::TestCase
4
+
5
+ context "Filtering Documents" do
6
+
7
+ should "filter through textile" do
8
+ doc = document('text.textile', :filter => 'textile')
9
+ source = doc.source(binding)
10
+ assert source.include?('\item')
11
+ end
12
+
13
+ should "not affect layouts" do
14
+ doc = document('text.textile',
15
+ :filter => 'textile',
16
+ :layout => "* layout\n* is\n<%= yield %>")
17
+ source = doc.source(binding)
18
+ assert source.include?("* layout"), "filtered layout"
19
+ assert source.include?('\item'), "didn't filter content"
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,50 @@
1
+ % This percent indicates a comment.
2
+ % This is a very simple latex article that introduces the way
3
+ % equations are typeset. Do this in linux:
4
+ %
5
+ % latex first.tex
6
+ % latex first.tex
7
+ % xdvi first.dvi
8
+ % dvips -o first.ps first.dvi
9
+ % gv first.ps
10
+ % lpr first.ps
11
+ % pdflatex first.tex
12
+ % acroread first.pdf
13
+ \documentclass[12pt]{article}
14
+ \title{First \LaTeX}
15
+ \author{Joe Student}
16
+ \date{\today}
17
+
18
+ \begin{document}
19
+
20
+ \maketitle
21
+
22
+ \abstract{This is a very simple example of using \LaTeX\ for typesetting.
23
+ The procedure for typesetting equations is introduced.}
24
+ \begin{sdsd}
25
+ \end{bar}
26
+
27
+ \section{A few equations}
28
+ Equations can be typeset inline like so: $\vec{F}=m\vec{a}$ .
29
+ Equations can also be separated form the text: $$ \vec{F}=m\vec{a} \ . $$
30
+ Notice the punctuation, the ``.'', had to be included with the equation.
31
+ Equations can also have a number assigned and a label attached,
32
+ as in the following:
33
+ \begin{equation}
34
+ \frac{D\vec{\omega}}{Dt} =
35
+ ( \vec{\omega} + \vec{\Omega}) \cdot \nabla \vec{U}
36
+ + \frac{1}{\rho^2} \nabla \rho \times \nabla p
37
+ + \nu \nabla^2 \vec{\omega}
38
+ \label{vorteq}
39
+ \end{equation}
40
+ The vorticity equation (\ref{vorteq}) is referenced by label,
41
+ not by number. The numbers may change as the document grows and
42
+ more equations are added.
43
+
44
+ New paragraphs are indicated with a blank line in the source code.
45
+
46
+ \section{The End}
47
+
48
+ Further examples of \LaTeX\ can be found at {\tt it.metr.ou.edu}
49
+
50
+ \end{document}
@@ -0,0 +1,48 @@
1
+ % This percent indicates a comment.
2
+ % This is a very simple latex article that introduces the way
3
+ % equations are typeset. Do this in linux:
4
+ %
5
+ % latex first.tex
6
+ % latex first.tex
7
+ % xdvi first.dvi
8
+ % dvips -o first.ps first.dvi
9
+ % gv first.ps
10
+ % lpr first.ps
11
+ % pdflatex first.tex
12
+ % acroread first.pdf
13
+ \documentclass[12pt]{article}
14
+ \title{First \LaTeX}
15
+ \author{<%= @author %>}
16
+ \date{\today}
17
+
18
+ \begin{document}
19
+
20
+ \maketitle
21
+
22
+ \abstract{This is a very simple example of using \LaTeX\ for typesetting.
23
+ The procedure for typesetting equations is introduced.}
24
+
25
+ \section{A few equations}
26
+ Equations can be typeset inline like so: $\vec{F}=m\vec{a}$ .
27
+ Equations can also be separated form the text: $$ \vec{F}=m\vec{a} \ . $$
28
+ Notice the punctuation, the ``.'', had to be included with the equation.
29
+ Equations can also have a number assigned and a label attached,
30
+ as in the following:
31
+ \begin{equation}
32
+ \frac{D\vec{\omega}}{Dt} =
33
+ ( \vec{\omega} + \vec{\Omega}) \cdot \nabla \vec{U}
34
+ + \frac{1}{\rho^2} \nabla \rho \times \nabla p
35
+ + \nu \nabla^2 \vec{\omega}
36
+ \label{vorteq}
37
+ \end{equation}
38
+ The vorticity equation (\ref{vorteq}) is referenced by label,
39
+ not by number. The numbers may change as the document grows and
40
+ more equations are added.
41
+
42
+ New paragraphs are indicated with a blank line in the source code.
43
+
44
+ \section{The End}
45
+
46
+ Further examples of \LaTeX\ can be found at {\tt it.metr.ou.edu}
47
+
48
+ \end{document}
@@ -0,0 +1 @@
1
+ \par <%= @name %>, Fragmented
@@ -0,0 +1,4 @@
1
+ This is a list:
2
+ * One
3
+ * Two
4
+ * _three_
@@ -0,0 +1,45 @@
1
+ require File.dirname(__FILE__) << '/test_helper'
2
+
3
+ class TempdirTest < Test::Unit::TestCase
4
+
5
+ context "Creating a temporary directory" do
6
+
7
+ setup do
8
+ change_tmpdir_for_testing
9
+ end
10
+
11
+ should "use a 'rtex' name prefix" do
12
+ RTeX::Tempdir.open do |tempdir|
13
+ assert_equal 'rtex-', File.basename(tempdir.path)[0,5]
14
+ end
15
+ end
16
+
17
+ should "remove the directory after use if no exception occurs by default" do
18
+ tempdir = nil
19
+ RTeX::Tempdir.open do |tempdir|
20
+ assert File.exists?(tempdir.path) # Check the temp dir exists
21
+ end
22
+ assert !File.exists?(tempdir.path) # Check the temp dir has been removed
23
+ end
24
+
25
+ should "return the result of the last statement if automatically removing the directory" do
26
+ result = RTeX::Tempdir.open do
27
+ :last
28
+ end
29
+ assert_equal result, :last
30
+ end
31
+
32
+ should "not remove the directory after use if an exception occurs" do
33
+ tempdir = nil
34
+ assert_raises RuntimeError do
35
+ RTeX::Tempdir.open do |tempdir|
36
+ assert File.directory?(tempdir.path)
37
+ raise "Test exception!"
38
+ end
39
+ end
40
+ assert File.directory?(tempdir.path)
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,26 @@
1
+ require 'test/unit'
2
+
3
+ require 'rubygems'
4
+ begin
5
+ require 'shoulda'
6
+ require 'flexmock/test_unit'
7
+ rescue LoadError
8
+ abort "the `Shoulda' and `flexmock' gems are required for testing"
9
+ end
10
+
11
+ require File.dirname(__FILE__) << '/../lib/rtex'
12
+
13
+ class Test::Unit::TestCase
14
+
15
+ def change_tmpdir_for_testing
16
+ flexmock(Dir).should_receive(:tmpdir).and_return(File.dirname(__FILE__) << '/tmp')
17
+ end
18
+
19
+ def document(name, options={})
20
+ name = name.kind_of?(Symbol) ? "#{name}.tex.erb" : name
21
+ template = File.read(File.dirname(__FILE__) << "/fixtures/#{name}")
22
+ RTeX::Document.new(template, {:tempdir => Dir.tmpdir}.merge(options))
23
+ end
24
+
25
+ end
26
+
@@ -0,0 +1,3 @@
1
+ Extracted from instiki, http://www.instiki.org
2
+ Copyright (c) David Heinemeier Hansson, Ruby License
3
+
@@ -0,0 +1,736 @@
1
+ # This is RedCloth (http://www.whytheluckystiff.net/ruby/redcloth/)
2
+ # converted by David Heinemeier Hansson to emit Tex
3
+
4
+ class String
5
+ # Flexible HTML escaping
6
+ def texesc!( mode )
7
+ gsub!( '&', '\\\\&' )
8
+ gsub!( '%', '\%' )
9
+ gsub!( '$', '\$' )
10
+ gsub!( '~', '$\sim$' )
11
+ end
12
+ end
13
+
14
+
15
+ def table_of_contents(text, pages)
16
+ text.gsub( /^([#*]+? .*?)$(?![^#*])/m ) do |match|
17
+ lines = match.split( /\n/ )
18
+ last_line = -1
19
+ depth = []
20
+ lines.each_with_index do |line, line_id|
21
+ if line =~ /^([#*]+) (.*)$/m
22
+ tl,content = $~[1..2]
23
+ content.gsub! /[\[\]]/, ""
24
+ content.strip!
25
+
26
+ if depth.last
27
+ if depth.last.length > tl.length
28
+ (depth.length - 1).downto(0) do |i|
29
+ break if depth[i].length == tl.length
30
+ lines[line_id - 1] << "" # "\n\t\\end{#{ lT( depth[i] ) }}\n\t"
31
+ depth.pop
32
+ end
33
+ end
34
+ if !depth.last.nil? && !tl.length.nil? && depth.last.length == tl.length
35
+ lines[line_id - 1] << ''
36
+ end
37
+ end
38
+
39
+ depth << tl unless depth.last == tl
40
+
41
+ subsection_depth = [depth.length - 1, 2].min
42
+
43
+ lines[line_id] = "\n\\#{ "sub" * subsection_depth }section{#{ content }}"
44
+ lines[line_id] += "\n#{pages[content]}" if pages.keys.include?(content)
45
+
46
+ lines[line_id] = "\\pagebreak\n#{lines[line_id]}" if subsection_depth == 0
47
+
48
+ last_line = line_id
49
+
50
+ elsif line =~ /^\s+\S/
51
+ last_line = line_id
52
+ elsif line_id - last_line < 2 and line =~ /^\S/
53
+ last_line = line_id
54
+ end
55
+ if line_id - last_line > 1 or line_id == lines.length - 1
56
+ depth.delete_if do |v|
57
+ lines[last_line] << "" # "\n\t\\end{#{ lT( v ) }}"
58
+ end
59
+ end
60
+ end
61
+ lines.join( "\n" )
62
+ end
63
+ end
64
+
65
+ class RedClothForTex < String
66
+
67
+ VERSION = '2.0.7'
68
+
69
+ #
70
+ # Mapping of 8-bit ASCII codes to HTML numerical entity equivalents.
71
+ # (from PyTextile)
72
+ #
73
+ TEXTILE_TAGS =
74
+
75
+ [[128, 8364], [129, 0], [130, 8218], [131, 402], [132, 8222], [133, 8230],
76
+ [134, 8224], [135, 8225], [136, 710], [137, 8240], [138, 352], [139, 8249],
77
+ [140, 338], [141, 0], [142, 0], [143, 0], [144, 0], [145, 8216], [146, 8217],
78
+ [147, 8220], [148, 8221], [149, 8226], [150, 8211], [151, 8212], [152, 732],
79
+ [153, 8482], [154, 353], [155, 8250], [156, 339], [157, 0], [158, 0], [159, 376]].
80
+
81
+ collect! do |a, b|
82
+ [a.chr, ( b.zero? and "" or "&#{ b };" )]
83
+ end
84
+
85
+ #
86
+ # Regular expressions to convert to HTML.
87
+ #
88
+ A_HLGN = /(?:(?:<>|<|>|\=|[()]+)+)/
89
+ A_VLGN = /[\-^~]/
90
+ C_CLAS = '(?:\([^)]+\))'
91
+ C_LNGE = '(?:\[[^\]]+\])'
92
+ C_STYL = '(?:\{[^}]+\})'
93
+ S_CSPN = '(?:\\\\\d+)'
94
+ S_RSPN = '(?:/\d+)'
95
+ A = "(?:#{A_HLGN}?#{A_VLGN}?|#{A_VLGN}?#{A_HLGN}?)"
96
+ S = "(?:#{S_CSPN}?#{S_RSPN}|#{S_RSPN}?#{S_CSPN}?)"
97
+ C = "(?:#{C_CLAS}?#{C_STYL}?#{C_LNGE}?|#{C_STYL}?#{C_LNGE}?#{C_CLAS}?|#{C_LNGE}?#{C_STYL}?#{C_CLAS}?)"
98
+ # PUNCT = Regexp::quote( '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' )
99
+ PUNCT = Regexp::quote( '!"#$%&\'*+,-./:;=?@\\^_`|~' )
100
+ HYPERLINK = '(\S+?)([^\w\s/;=\?]*?)(\s|$)'
101
+
102
+ GLYPHS = [
103
+ # [ /([^\s\[{(>])?\'([dmst]\b|ll\b|ve\b|\s|:|$)/, '\1&#8217;\2' ], # single closing
104
+ [ /([^\s\[{(>])\'/, '\1&#8217;' ], # single closing
105
+ [ /\'(?=\s|s\b|[#{PUNCT}])/, '&#8217;' ], # single closing
106
+ [ /\'/, '&#8216;' ], # single opening
107
+ # [ /([^\s\[{(])?"(\s|:|$)/, '\1&#8221;\2' ], # double closing
108
+ [ /([^\s\[{(>])"/, '\1&#8221;' ], # double closing
109
+ [ /"(?=\s|[#{PUNCT}])/, '&#8221;' ], # double closing
110
+ [ /"/, '&#8220;' ], # double opening
111
+ [ /\b( )?\.{3}/, '\1&#8230;' ], # ellipsis
112
+ [ /\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])/, '<acronym title="\2">\1</acronym>' ], # 3+ uppercase acronym
113
+ [ /(^|[^"][>\s])([A-Z][A-Z0-9 ]{2,})([^<a-z0-9]|$)/, '\1<span class="caps">\2</span>\3' ], # 3+ uppercase caps
114
+ [ /(\.\s)?\s?--\s?/, '\1&#8212;' ], # em dash
115
+ [ /\s->\s/, ' &rarr; ' ], # en dash
116
+ [ /\s-\s/, ' &#8211; ' ], # en dash
117
+ [ /(\d+) ?x ?(\d+)/, '\1&#215;\2' ], # dimension sign
118
+ [ /\b ?[(\[]TM[\])]/i, '&#8482;' ], # trademark
119
+ [ /\b ?[(\[]R[\])]/i, '&#174;' ], # registered
120
+ [ /\b ?[(\[]C[\])]/i, '&#169;' ] # copyright
121
+ ]
122
+
123
+ I_ALGN_VALS = {
124
+ '<' => 'left',
125
+ '=' => 'center',
126
+ '>' => 'right'
127
+ }
128
+
129
+ H_ALGN_VALS = {
130
+ '<' => 'left',
131
+ '=' => 'center',
132
+ '>' => 'right',
133
+ '<>' => 'justify'
134
+ }
135
+
136
+ V_ALGN_VALS = {
137
+ '^' => 'top',
138
+ '-' => 'middle',
139
+ '~' => 'bottom'
140
+ }
141
+
142
+ QTAGS = [
143
+ ['**', 'bf'],
144
+ ['*', 'bf'],
145
+ ['??', 'cite'],
146
+ ['-', 'del'],
147
+ ['__', 'underline'],
148
+ ['_', 'em'],
149
+ ['%', 'span'],
150
+ ['+', 'ins'],
151
+ ['^', 'sup'],
152
+ ['~', 'sub']
153
+ ]
154
+
155
+ def self.available?
156
+ if not defined? @@available
157
+ begin
158
+ @@available = system "pdflatex -version"
159
+ rescue Errno::ENOENT
160
+ @@available = false
161
+ end
162
+ end
163
+ @@available
164
+ end
165
+
166
+ #
167
+ # Two accessor for setting security restrictions.
168
+ #
169
+ # This is a nice thing if you're using RedCloth for
170
+ # formatting in public places (e.g. Wikis) where you
171
+ # don't want users to abuse HTML for bad things.
172
+ #
173
+ # If +:filter_html+ is set, HTML which wasn't
174
+ # created by the Textile processor will be escaped.
175
+ #
176
+ # If +:filter_styles+ is set, it will also disable
177
+ # the style markup specifier. ('{color: red}')
178
+ #
179
+ attr_accessor :filter_html, :filter_styles
180
+
181
+ #
182
+ # Accessor for toggling line folding.
183
+ #
184
+ # If +:fold_lines+ is set, single newlines will
185
+ # not be converted to break tags.
186
+ #
187
+ attr_accessor :fold_lines
188
+
189
+ def initialize( string, restrictions = [] )
190
+ restrictions.each { |r| method( "#{ r }=" ).call( true ) }
191
+ super( string )
192
+ end
193
+
194
+ #
195
+ # Generate tex.
196
+ #
197
+ def to_tex( lite = false )
198
+
199
+ # make our working copy
200
+ text = self.dup
201
+
202
+ @urlrefs = {}
203
+ @shelf = []
204
+
205
+ # incoming_entities text
206
+ fix_entities text
207
+ clean_white_space text
208
+
209
+ get_refs text
210
+
211
+ no_textile text
212
+
213
+ unless lite
214
+ lists text
215
+ table text
216
+ end
217
+
218
+ glyphs text
219
+
220
+ unless lite
221
+ fold text
222
+ block text
223
+ end
224
+
225
+ retrieve text
226
+ encode_entities text
227
+
228
+ text.gsub!(/\[\[(.*?)\]\]/, "\\1")
229
+ text.gsub!(/_/, "\\_")
230
+ text.gsub!( /<\/?notextile>/, '' )
231
+ # text.gsub!( /x%x%/, '&#38;' )
232
+ # text.gsub!( /<br \/>/, "<br />\n" )
233
+ text.strip!
234
+ text
235
+
236
+ end
237
+
238
+ def pgl( text )
239
+ GLYPHS.each do |re, resub|
240
+ text.gsub! re, resub
241
+ end
242
+ end
243
+
244
+ def pba( text_in, element = "" )
245
+
246
+ return '' unless text_in
247
+
248
+ style = []
249
+ text = text_in.dup
250
+ if element == 'td'
251
+ colspan = $1 if text =~ /\\(\d+)/
252
+ rowspan = $1 if text =~ /\/(\d+)/
253
+ style << "vertical-align:#{ v_align( $& ) };" if text =~ A_VLGN
254
+ end
255
+
256
+ style << "#{ $1 };" if not @filter_styles and
257
+ text.sub!( /\{([^}]*)\}/, '' )
258
+
259
+ lang = $1 if
260
+ text.sub!( /\[([^)]+?)\]/, '' )
261
+
262
+ cls = $1 if
263
+ text.sub!( /\(([^()]+?)\)/, '' )
264
+
265
+ style << "padding-left:#{ $1.length }em;" if
266
+ text.sub!( /([(]+)/, '' )
267
+
268
+ style << "padding-right:#{ $1.length }em;" if text.sub!( /([)]+)/, '' )
269
+
270
+ style << "text-align:#{ h_align( $& ) };" if text =~ A_HLGN
271
+
272
+ cls, id = $1, $2 if cls =~ /^(.*?)#(.*)$/
273
+
274
+ atts = ''
275
+ atts << " style=\"#{ style.join }\"" unless style.empty?
276
+ atts << " class=\"#{ cls }\"" unless cls.to_s.empty?
277
+ atts << " lang=\"#{ lang }\"" if lang
278
+ atts << " id=\"#{ id }\"" if id
279
+ atts << " colspan=\"#{ colspan }\"" if colspan
280
+ atts << " rowspan=\"#{ rowspan }\"" if rowspan
281
+
282
+ atts
283
+ end
284
+
285
+ def table( text )
286
+ text << "\n\n"
287
+ text.gsub!( /^(?:table(_?#{S}#{A}#{C})\. ?\n)?^(#{A}#{C}\.? ?\|.*?\|)\n\n/m ) do |matches|
288
+
289
+ tatts, fullrow = $~[1..2]
290
+ tatts = pba( tatts, 'table' )
291
+ rows = []
292
+
293
+ fullrow.
294
+ split( /\|$/m ).
295
+ delete_if { |x| x.empty? }.
296
+ each do |row|
297
+
298
+ ratts, row = pba( $1, 'tr' ), $2 if row =~ /^(#{A}#{C}\. )(.*)/m
299
+
300
+ cells = []
301
+ row.split( '|' ).each do |cell|
302
+ ctyp = 'd'
303
+ ctyp = 'h' if cell =~ /^_/
304
+
305
+ catts = ''
306
+ catts, cell = pba( $1, 'td' ), $2 if cell =~ /^(_?#{S}#{A}#{C}\. )(.*)/
307
+
308
+ unless cell.strip.empty?
309
+ cells << "\t\t\t<t#{ ctyp }#{ catts }>#{ cell }</t#{ ctyp }>"
310
+ end
311
+ end
312
+ rows << "\t\t<tr#{ ratts }>\n#{ cells.join( "\n" ) }\n\t\t</tr>"
313
+ end
314
+ "\t<table#{ tatts }>\n#{ rows.join( "\n" ) }\n\t</table>\n\n"
315
+ end
316
+ end
317
+
318
+ def lists( text )
319
+ text.gsub!( /^([#*]+?#{C} .*?)$(?![^#*])/m ) do |match|
320
+ lines = match.split( /\n/ )
321
+ last_line = -1
322
+ depth = []
323
+ lines.each_with_index do |line, line_id|
324
+ if line =~ /^([#*]+)(#{A}#{C}) (.*)$/m
325
+ tl,atts,content = $~[1..3]
326
+ if depth.last
327
+ if depth.last.length > tl.length
328
+ (depth.length - 1).downto(0) do |i|
329
+ break if depth[i].length == tl.length
330
+ lines[line_id - 1] << "\n\t\\end{#{ lT( depth[i] ) }}\n\t"
331
+ depth.pop
332
+ end
333
+ end
334
+ if !depth.last.nil? && !tl.length.nil? && depth.last.length == tl.length
335
+ lines[line_id - 1] << ''
336
+ end
337
+ end
338
+ unless depth.last == tl
339
+ depth << tl
340
+ atts = pba( atts )
341
+ lines[line_id] = "\t\\begin{#{ lT(tl) }}\n\t\\item #{ content }"
342
+ else
343
+ lines[line_id] = "\t\t\\item #{ content }"
344
+ end
345
+ last_line = line_id
346
+
347
+ elsif line =~ /^\s+\S/
348
+ last_line = line_id
349
+ elsif line_id - last_line < 2 and line =~ /^\S/
350
+ last_line = line_id
351
+ end
352
+ if line_id - last_line > 1 or line_id == lines.length - 1
353
+ depth.delete_if do |v|
354
+ lines[last_line] << "\n\t\\end{#{ lT( v ) }}"
355
+ end
356
+ end
357
+ end
358
+ lines.join( "\n" )
359
+ end
360
+ end
361
+
362
+ def lT( text )
363
+ text =~ /\#$/ ? 'enumerate' : 'itemize'
364
+ end
365
+
366
+ def fold( text )
367
+ text.gsub!( /(.+)\n(?![#*\s|])/, "\\1\\\\\\\\" )
368
+ # text.gsub!( /(.+)\n(?![#*\s|])/, "\\1#{ @fold_lines ? ' ' : '<br />' }" )
369
+ end
370
+
371
+ def block( text )
372
+ pre = false
373
+ find = ['bq','h[1-6]','fn\d+']
374
+
375
+ regexp_cue = []
376
+
377
+ lines = text.split( /\n/ ) + [' ']
378
+ new_text =
379
+ lines.collect do |line|
380
+ pre = true if line =~ /<(pre|notextile)>/i
381
+ find.each do |tag|
382
+ line.gsub!( /^(#{ tag })(#{A}#{C})\.(?::(\S+))? (.*)$/ ) do |m|
383
+ tag,atts,cite,content = $~[1..4]
384
+
385
+ atts = pba( atts )
386
+
387
+ if tag =~ /fn(\d+)/
388
+ # tag = 'p';
389
+ # atts << " id=\"fn#{ $1 }\""
390
+ regexp_cue << [ /footnote\{#{$1}}/, "footnote{#{content}}" ]
391
+ content = ""
392
+ end
393
+
394
+ if tag =~ /h([1-6])/
395
+ section_type = "sub" * [$1.to_i - 1, 2].min
396
+ start = "\t\\#{section_type}section*{"
397
+ tend = "}"
398
+ end
399
+
400
+ if tag == "bq"
401
+ cite = check_refs( cite )
402
+ cite = " cite=\"#{ cite }\"" if cite
403
+ start = "\t\\begin{quotation}\n\\noindent {\\em ";
404
+ tend = "}\n\t\\end{quotation}";
405
+ end
406
+
407
+ "#{ start }#{ content }#{ tend }"
408
+ end unless pre
409
+ end
410
+
411
+ #line.gsub!( /^(?!\t|<\/?pre|<\/?notextile|<\/?code|$| )(.*)/, "\t<p>\\1</p>" )
412
+
413
+ #line.gsub!( "<br />", "\n" ) if pre
414
+ # pre = false if line =~ /<\/(pre|notextile)>/i
415
+
416
+ line
417
+ end.join( "\n" )
418
+ text.replace( new_text )
419
+ regexp_cue.each { |pair| text.gsub!(pair.first, pair.last) }
420
+ end
421
+
422
+ def span( text )
423
+ QTAGS.each do |tt, ht|
424
+ ttr = Regexp::quote( tt )
425
+ text.gsub!(
426
+
427
+ /(^|\s|\>|[#{PUNCT}{(\[])
428
+ #{ttr}
429
+ (#{C})
430
+ (?::(\S+?))?
431
+ ([^\s#{ttr}]+?(?:[^\n]|\n(?!\n))*?)
432
+ ([#{PUNCT}]*?)
433
+ #{ttr}
434
+ (?=[\])}]|[#{PUNCT}]+?|<|\s|$)/xm
435
+
436
+ ) do |m|
437
+
438
+ start,atts,cite,content,tend = $~[1..5]
439
+ atts = pba( atts )
440
+ atts << " cite=\"#{ cite }\"" if cite
441
+
442
+ "#{ start }{\\#{ ht } #{ content }#{ tend }}"
443
+
444
+ end
445
+ end
446
+ end
447
+
448
+ def links( text )
449
+ text.gsub!( /
450
+ ([\s\[{(]|[#{PUNCT}])? # $pre
451
+ " # start
452
+ (#{C}) # $atts
453
+ ([^"]+?) # $text
454
+ \s?
455
+ (?:\(([^)]+?)\)(?="))? # $title
456
+ ":
457
+ (\S+?) # $url
458
+ (\/)? # $slash
459
+ ([^\w\/;]*?) # $post
460
+ (?=\s|$)
461
+ /x ) do |m|
462
+ pre,atts,text,title,url,slash,post = $~[1..7]
463
+
464
+ url.gsub!(/(\\)(.)/, '\2')
465
+ url = check_refs( url )
466
+
467
+ atts = pba( atts )
468
+ atts << " title=\"#{ title }\"" if title
469
+ atts = shelve( atts ) if atts
470
+
471
+ "#{ pre }\\textit{#{ text }} \\footnote{\\texttt{\\textless #{ url }#{ slash }" +
472
+ "\\textgreater}#{ post }}"
473
+ end
474
+ end
475
+
476
+ def get_refs( text )
477
+ text.gsub!( /(^|\s)\[(.+?)\]((?:http:\/\/|javascript:|ftp:\/\/|\/)\S+?)(?=\s|$)/ ) do |m|
478
+ flag, url = $~[1..2]
479
+ @urlrefs[flag] = url
480
+ end
481
+ end
482
+
483
+ def check_refs( text )
484
+ @urlrefs[text] || text
485
+ end
486
+
487
+ def image( text )
488
+ text.gsub!( /
489
+ \! # opening
490
+ (\<|\=|\>)? # optional alignment atts
491
+ (#{C}) # optional style,class atts
492
+ (?:\. )? # optional dot-space
493
+ ([^\s(!]+?) # presume this is the src
494
+ \s? # optional space
495
+ (?:\(((?:[^\(\)]|\([^\)]+\))+?)\))? # optional title
496
+ \! # closing
497
+ (?::#{ HYPERLINK })? # optional href
498
+ /x ) do |m|
499
+ algn,atts,url,title,href,href_a1,href_a2 = $~[1..7]
500
+ atts = pba( atts )
501
+ atts << " align=\"#{ i_align( algn ) }\"" if algn
502
+ atts << " title=\"#{ title }\"" if title
503
+ atts << " alt=\"#{ title }\""
504
+ # size = @getimagesize($url);
505
+ # if($size) $atts.= " $size[3]";
506
+
507
+ href = check_refs( href ) if href
508
+ url = check_refs( url )
509
+
510
+ out = ''
511
+ out << "<a href=\"#{ href }\">" if href
512
+ out << "<img src=\"#{ url }\"#{ atts } />"
513
+ out << "</a>#{ href_a1 }#{ href_a2 }" if href
514
+
515
+ out
516
+ end
517
+ end
518
+
519
+ def code( text )
520
+ text.gsub!( /
521
+ (?:^|([\s\(\[{])) # 1 open bracket?
522
+ @ # opening
523
+ (?:\|(\w+?)\|)? # 2 language
524
+ (\S(?:[^\n]|\n(?!\n))*?) # 3 code
525
+ @ # closing
526
+ (?:$|([\]})])|
527
+ (?=[#{PUNCT}]{1,2}|
528
+ \s)) # 4 closing bracket?
529
+ /x ) do |m|
530
+ before,lang,code,after = $~[1..4]
531
+ lang = " language=\"#{ lang }\"" if lang
532
+ "#{ before }<code#{ lang }>#{ code }</code>#{ after }"
533
+ end
534
+ end
535
+
536
+ def shelve( val )
537
+ @shelf << val
538
+ " <#{ @shelf.length }>"
539
+ end
540
+
541
+ def retrieve( text )
542
+ @shelf.each_with_index do |r, i|
543
+ text.gsub!( " <#{ i + 1 }>", r )
544
+ end
545
+ end
546
+
547
+ def incoming_entities( text )
548
+ ## turn any incoming ampersands into a dummy character for now.
549
+ ## This uses a negative lookahead for alphanumerics followed by a semicolon,
550
+ ## implying an incoming html entity, to be skipped
551
+
552
+ text.gsub!( /&(?![#a-z0-9]+;)/i, "x%x%" )
553
+ end
554
+
555
+ def encode_entities( text )
556
+ ## Convert high and low ascii to entities.
557
+ # if $-K == "UTF-8"
558
+ # encode_high( text )
559
+ # else
560
+ text.texesc!( :NoQuotes )
561
+ # end
562
+ end
563
+
564
+ def fix_entities( text )
565
+ ## de-entify any remaining angle brackets or ampersands
566
+ text.gsub!( "\&", "&" )
567
+ text.gsub!( "\%", "%" )
568
+ end
569
+
570
+ def clean_white_space( text )
571
+ text.gsub!( /\r\n/, "\n" )
572
+ text.gsub!( /\t/, '' )
573
+ text.gsub!( /\n{3,}/, "\n\n" )
574
+ text.gsub!( /\n *\n/, "\n\n" )
575
+ text.gsub!( /"$/, "\" " )
576
+ end
577
+
578
+ def no_textile( text )
579
+ text.gsub!( /(^|\s)==(.*?)==(\s|$)?/,
580
+ '\1<notextile>\2</notextile>\3' )
581
+ end
582
+
583
+ def footnote_ref( text )
584
+ text.gsub!( /\[([0-9]+?)\](\s)?/,
585
+ '\footnote{\1}\2')
586
+ #'<sup><a href="#fn\1">\1</a></sup>\2' )
587
+ end
588
+
589
+ def inline( text )
590
+ image text
591
+ links text
592
+ code text
593
+ span text
594
+ end
595
+
596
+ def glyphs_deep( text )
597
+ codepre = 0
598
+ offtags = /(?:code|pre|kbd|notextile)/
599
+ if text !~ /<.*>/
600
+ # pgl text
601
+ footnote_ref text
602
+ else
603
+ used_offtags = {}
604
+ text.gsub!( /(?:[^<].*?(?=<[^\n]*?>|$)|<[^\n]*?>+)/m ) do |line|
605
+ tagline = ( line =~ /^<.*>/ )
606
+
607
+ ## matches are off if we're between <code>, <pre> etc.
608
+ if tagline
609
+ if line =~ /<(#{ offtags })>/i
610
+ codepre += 1
611
+ used_offtags[$1] = true
612
+ line.texesc!( :NoQuotes ) if codepre - used_offtags.length > 0
613
+ elsif line =~ /<\/(#{ offtags })>/i
614
+ line.texesc!( :NoQuotes ) if codepre - used_offtags.length > 0
615
+ codepre -= 1 unless codepre.zero?
616
+ used_offtags = {} if codepre.zero?
617
+ elsif @filter_html or codepre > 0
618
+ line.texesc!( :NoQuotes )
619
+ ## line.gsub!( /&lt;(\/?#{ offtags })&gt;/, '<\1>' )
620
+ end
621
+ ## do htmlspecial if between <code>
622
+ elsif codepre > 0
623
+ line.texesc!( :NoQuotes )
624
+ ## line.gsub!( /&lt;(\/?#{ offtags })&gt;/, '<\1>' )
625
+ elsif not tagline
626
+ inline line
627
+ glyphs_deep line
628
+ end
629
+
630
+ line
631
+ end
632
+ end
633
+ end
634
+
635
+ def glyphs( text )
636
+ text.gsub!( /"\z/, "\" " )
637
+ ## if no html, do a simple search and replace...
638
+ if text !~ /<.*>/
639
+ inline text
640
+ end
641
+ glyphs_deep text
642
+ end
643
+
644
+ def i_align( text )
645
+ I_ALGN_VALS[text]
646
+ end
647
+
648
+ def h_align( text )
649
+ H_ALGN_VALS[text]
650
+ end
651
+
652
+ def v_align( text )
653
+ V_ALGN_VALS[text]
654
+ end
655
+
656
+ def encode_high( text )
657
+ ## mb_encode_numericentity($text, $cmap, $charset);
658
+ end
659
+
660
+ def decode_high( text )
661
+ ## mb_decode_numericentity($text, $cmap, $charset);
662
+ end
663
+
664
+ def textile_popup_help( name, helpvar, windowW, windowH )
665
+ ' <a target="_blank" href="http://www.textpattern.com/help/?item=' + helpvar + '" onclick="window.open(this.href, \'popupwindow\', \'width=' + windowW + ',height=' + windowH + ',scrollbars,resizable\'); return false;">' + name + '</a><br />'
666
+ end
667
+
668
+ CMAP = [
669
+ 160, 255, 0, 0xffff,
670
+ 402, 402, 0, 0xffff,
671
+ 913, 929, 0, 0xffff,
672
+ 931, 937, 0, 0xffff,
673
+ 945, 969, 0, 0xffff,
674
+ 977, 978, 0, 0xffff,
675
+ 982, 982, 0, 0xffff,
676
+ 8226, 8226, 0, 0xffff,
677
+ 8230, 8230, 0, 0xffff,
678
+ 8242, 8243, 0, 0xffff,
679
+ 8254, 8254, 0, 0xffff,
680
+ 8260, 8260, 0, 0xffff,
681
+ 8465, 8465, 0, 0xffff,
682
+ 8472, 8472, 0, 0xffff,
683
+ 8476, 8476, 0, 0xffff,
684
+ 8482, 8482, 0, 0xffff,
685
+ 8501, 8501, 0, 0xffff,
686
+ 8592, 8596, 0, 0xffff,
687
+ 8629, 8629, 0, 0xffff,
688
+ 8656, 8660, 0, 0xffff,
689
+ 8704, 8704, 0, 0xffff,
690
+ 8706, 8707, 0, 0xffff,
691
+ 8709, 8709, 0, 0xffff,
692
+ 8711, 8713, 0, 0xffff,
693
+ 8715, 8715, 0, 0xffff,
694
+ 8719, 8719, 0, 0xffff,
695
+ 8721, 8722, 0, 0xffff,
696
+ 8727, 8727, 0, 0xffff,
697
+ 8730, 8730, 0, 0xffff,
698
+ 8733, 8734, 0, 0xffff,
699
+ 8736, 8736, 0, 0xffff,
700
+ 8743, 8747, 0, 0xffff,
701
+ 8756, 8756, 0, 0xffff,
702
+ 8764, 8764, 0, 0xffff,
703
+ 8773, 8773, 0, 0xffff,
704
+ 8776, 8776, 0, 0xffff,
705
+ 8800, 8801, 0, 0xffff,
706
+ 8804, 8805, 0, 0xffff,
707
+ 8834, 8836, 0, 0xffff,
708
+ 8838, 8839, 0, 0xffff,
709
+ 8853, 8853, 0, 0xffff,
710
+ 8855, 8855, 0, 0xffff,
711
+ 8869, 8869, 0, 0xffff,
712
+ 8901, 8901, 0, 0xffff,
713
+ 8968, 8971, 0, 0xffff,
714
+ 9001, 9002, 0, 0xffff,
715
+ 9674, 9674, 0, 0xffff,
716
+ 9824, 9824, 0, 0xffff,
717
+ 9827, 9827, 0, 0xffff,
718
+ 9829, 9830, 0, 0xffff,
719
+ 338, 339, 0, 0xffff,
720
+ 352, 353, 0, 0xffff,
721
+ 376, 376, 0, 0xffff,
722
+ 710, 710, 0, 0xffff,
723
+ 732, 732, 0, 0xffff,
724
+ 8194, 8195, 0, 0xffff,
725
+ 8201, 8201, 0, 0xffff,
726
+ 8204, 8207, 0, 0xffff,
727
+ 8211, 8212, 0, 0xffff,
728
+ 8216, 8218, 0, 0xffff,
729
+ 8218, 8218, 0, 0xffff,
730
+ 8220, 8222, 0, 0xffff,
731
+ 8224, 8225, 0, 0xffff,
732
+ 8240, 8240, 0, 0xffff,
733
+ 8249, 8250, 0, 0xffff,
734
+ 8364, 8364, 0, 0xffff
735
+ ]
736
+ end