org-ruby 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,13 @@
1
+ == 0.8.0 / 2013-02-10
2
+
3
+ * A lot of refactoring work and bugfixes contributed by vonavi (many thanks!)
4
+ * Raw HTML is supported with #+html
5
+ * Code indentation for code blocks is fixed now
6
+ * Support for definition lists is improved
7
+ * Bugfix for when including headlines in center and quote blocks.
8
+ * Indentation of HTML output improved
9
+ * Improvements to entities support for Textile and HTML outputs
10
+
1
11
  == 0.7.2 / 2012-10-07
2
12
 
3
13
  * Many fixes to the regular expressions used for emphasis, contributed by [[http://github.com/vonavi][vonavi]]
data/README.rdoc CHANGED
@@ -1,68 +1,72 @@
1
- org-ruby
2
- by Brian Dewey
3
- http://github.com/bdewey/org-ruby
1
+ = org-ruby
2
+ <em>Originally by Brian Dewey</em> (http://github.com/bdewey/org-ruby)
4
3
 
5
- == DESCRIPTION:
4
+ {<img src="https://secure.travis-ci.org/wallyqs/org-ruby.png?branch=master" alt="Build Status" />}[http://travis-ci.org/wallyqs/org-ruby]
6
5
 
7
- This gem contains Ruby routines for parsing org-mode files.The most
8
- significant thing this library does today is convert org-mode files to
9
- HTML or textile. Currently, you cannot do much to customize the
10
- conversion. The supplied textile conversion is optimized for
11
- extracting "content" from the orgfile as opposed to "metadata."
6
+ An {org-mode}[http://orgmode.org] parser written in Ruby. The most significant thing this library does today is convert org-mode files to HTML or Textile.
7
+ Currently, you cannot do much to customize the conversion. The supplied textile conversion is optimized for extracting
8
+ “content” from the orgfile as opposed to “metadata.”
12
9
 
13
- == FEATURES/PROBLEMS:
10
+ == Installation
14
11
 
15
- * Converts org-mode files to HTML or Textile
16
- * Supports tables, block quotes, and block code
17
- * Supports bold, italic, underline, strikethrough, and code inline formatting.
18
- * Supports hyperlinks that are in double-brackets
19
- * Supports +.org+ views in Rails through Tilt.
20
- * Code syntax highlight of code blocks using Pygments.rb or Coderay when available
21
- * Upcoming: Handle export options specified in the org buffer.
22
-
23
- == SYNOPSIS:
24
-
25
- From the command line:
26
-
27
- org-ruby sample.org
28
-
29
- ...will output a HTML version of sample.org.
30
-
31
- org-ruby --translate textile sample.org
12
+ gem install org-ruby
32
13
 
33
- ...will output a textile version of sample.org.
14
+ == Usage
34
15
 
35
16
  From Ruby code:
36
17
 
37
- Orgmode::Parser.new(data)
18
+ require 'org-ruby'
19
+
20
+ # Renders HTML
21
+ Orgmode::Parser.new("* Hello world!).to_html
22
+ # => "<h1>Hello world!</h1>\n"
38
23
 
39
- ...will construct a new +Parser+ object.
24
+ # Renders Textile
25
+ Orgmode::Parser.new("* Hello world!).to_textile
26
+ # => "h1. Hello world!\n"
40
27
 
41
- == INSTALL:
28
+ It can also be used from the command line:
42
29
 
43
- sudo gem install org-ruby
30
+ org-ruby sample.org --translate html
44
31
 
45
- == LICENSE:
32
+ ...will output a HTML version of sample.org.
46
33
 
47
- (The MIT License)
34
+ org-ruby --translate textile sample.org
48
35
 
49
- Copyright (c) 2009 Brian Dewey
36
+ ...will output a textile version of sample.org.
50
37
 
51
- Permission is hereby granted, free of charge, to any person obtaining
52
- a copy of this software and associated documentation files (the
53
- 'Software'), to deal in the Software without restriction, including
54
- without limitation the rights to use, copy, modify, merge, publish,
55
- distribute, sublicense, and/or sell copies of the Software, and to
56
- permit persons to whom the Software is furnished to do so, subject to
57
- the following conditions:
38
+ == Features
58
39
 
59
- The above copyright notice and this permission notice shall be
60
- included in all copies or substantial portions of the Software.
40
+ * Converts org-mode files to HTML or Textile
41
+ * Supports tables, block quotes, code blocks, and html blocks
42
+ * Supports bold, italic, underline, strikethrough, and code inline formatting.
43
+ * Supports hyperlinks that are in double-brackets
44
+ * Supports definition lists
45
+ * Supports footnotes
46
+ * Supports +.org+ views in Rails through Tilt.
47
+ * Code syntax highlight of code blocks using Pygments.rb or Coderay when available
61
48
 
62
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
63
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
64
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
65
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
66
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
67
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
68
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
49
+ == License
50
+
51
+ (The MIT License)
52
+
53
+ Copyright (c) 2009 Brian Dewey
54
+
55
+ Permission is hereby granted, free of charge, to any person obtaining
56
+ a copy of this software and associated documentation files (the
57
+ 'Software'), to deal in the Software without restriction, including
58
+ without limitation the rights to use, copy, modify, merge, publish,
59
+ distribute, sublicense, and/or sell copies of the Software, and to
60
+ permit persons to whom the Software is furnished to do so, subject to
61
+ the following conditions:
62
+
63
+ The above copyright notice and this permission notice shall be
64
+ included in all copies or substantial portions of the Software.
65
+
66
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
67
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
68
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
69
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
70
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
71
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
72
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/lib/org-ruby.rb CHANGED
@@ -21,7 +21,7 @@ require 'org-ruby/tilt'
21
21
  module OrgRuby
22
22
 
23
23
  # :stopdoc:
24
- VERSION = '0.7.2'
24
+ VERSION = '0.8.0'
25
25
  LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
26
26
  PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
27
27
  # :startdoc:
@@ -45,7 +45,6 @@ module Orgmode
45
45
  def initialize(line, parser = nil, offset=0)
46
46
  super(line, parser)
47
47
  @body_lines = []
48
- @body_lines << self # Make @body_lines contain the headline?
49
48
  @tags = []
50
49
  @export_state = :exclude
51
50
  if (@line =~ LineRegexp) then
@@ -15,12 +15,19 @@ module Orgmode
15
15
 
16
16
  HtmlBlockTag = {
17
17
  :paragraph => "p",
18
- :ordered_list => "li",
19
- :unordered_list => "li",
18
+ :ordered_list => "ol",
19
+ :unordered_list => "ul",
20
+ :list_item => "li",
21
+ :definition_list => "dl",
20
22
  :definition_term => "dt",
21
23
  :definition_descr => "dd",
24
+ :table => "table",
22
25
  :table_row => "tr",
23
- :table_header => "tr",
26
+ :quote => "blockquote",
27
+ :example => "pre",
28
+ :src => "pre",
29
+ :inline_example => "pre",
30
+ :center => "div",
24
31
  :heading1 => "h1",
25
32
  :heading2 => "h2",
26
33
  :heading3 => "h3",
@@ -29,18 +36,6 @@ module Orgmode
29
36
  :heading6 => "h6"
30
37
  }
31
38
 
32
- ModeTag = {
33
- :unordered_list => "ul",
34
- :ordered_list => "ol",
35
- :definition_list => "dl",
36
- :table => "table",
37
- :blockquote => "blockquote",
38
- :example => "pre",
39
- :src => "pre",
40
- :inline_example => "pre",
41
- :center => "div"
42
- }
43
-
44
39
  attr_reader :options
45
40
 
46
41
  def initialize(output, opts = {})
@@ -50,167 +45,160 @@ module Orgmode
50
45
  else
51
46
  @title_decoration = ""
52
47
  end
48
+ @buffer_tag = "HTML"
53
49
  @options = opts
50
+ @new_paragraph = :start
54
51
  @footnotes = {}
55
52
  @unclosed_tags = []
56
53
  @logger.debug "HTML export options: #{@options.inspect}"
57
54
  end
58
55
 
59
56
  # Output buffer is entering a new mode. Use this opportunity to
60
- # write out one of the block tags in the ModeTag constant to put
61
- # this information in the HTML stream.
62
- def push_mode(mode)
63
- if ModeTag[mode] then
64
- output_indentation
65
- css_class = ""
66
- css_class = " class=\"src\"" if mode == :src and @block_lang.empty?
67
- css_class = " class=\"src src-#{@block_lang}\"" if mode == :src and not @block_lang.empty?
68
- css_class = " class=\"example\"" if (mode == :example || mode == :inline_example)
69
- css_class = " style=\"text-align: center\"" if mode == :center
70
-
71
- unless ((mode == :table and skip_tables?) or
57
+ # write out one of the block tags in the HtmlBlockTag constant to
58
+ # put this information in the HTML stream.
59
+ def push_mode(mode, indent)
60
+ super(mode)
61
+ @list_indent_stack.push(indent)
62
+
63
+ if HtmlBlockTag[mode]
64
+ unless ((mode_is_table?(mode) and skip_tables?) or
72
65
  (mode == :src and defined? Pygments))
73
- @logger.debug "#{mode}: <#{ModeTag[mode]}#{css_class}>\n"
74
- @output << "<#{ModeTag[mode]}#{css_class}>\n"
66
+ css_class = case
67
+ when (mode == :src and @block_lang.empty?)
68
+ " class=\"src\""
69
+ when (mode == :src and not @block_lang.empty?)
70
+ " class=\"src src-#{@block_lang}\""
71
+ when (mode == :example || mode == :inline_example)
72
+ " class=\"example\""
73
+ when mode == :center
74
+ " style=\"text-align: center\""
75
+ else
76
+ @title_decoration
77
+ end
78
+
79
+ add_paragraph unless @new_paragraph == :start
80
+ @new_paragraph = true
81
+
82
+ @logger.debug "#{mode}: <#{HtmlBlockTag[mode]}#{css_class}>"
83
+ @output << "<#{HtmlBlockTag[mode]}#{css_class}>"
84
+ # Entering a new mode obliterates the title decoration
85
+ @title_decoration = ""
75
86
  end
76
- # Entering a new mode obliterates the title decoration
77
- @title_decoration = ""
78
87
  end
79
- super(mode)
80
88
  end
81
89
 
82
90
  # We are leaving a mode. Close any tags that were opened when
83
91
  # entering this mode.
84
92
  def pop_mode(mode = nil)
85
93
  m = super(mode)
86
- if ModeTag[m] then
87
- output_indentation
88
- # Need to close the floating li elements before closing the list
89
- if (m == :unordered_list or
90
- m == :ordered_list or
91
- m == :definition_list) and
92
- (not @unclosed_tags.empty?)
93
- close_floating_li_tags
94
- end
95
-
96
- unless ((mode == :table and skip_tables?) or
97
- (mode == :src and defined? Pygments))
98
- @logger.debug "</#{ModeTag[m]}>\n"
99
- @output << "</#{ModeTag[m]}>\n"
94
+ if HtmlBlockTag[m]
95
+ unless ((mode_is_table?(m) and skip_tables?) or
96
+ (m == :src and defined? Pygments))
97
+ add_paragraph if @new_paragraph
98
+ @new_paragraph = true
99
+ @logger.debug "</#{HtmlBlockTag[m]}>"
100
+ @output << "</#{HtmlBlockTag[m]}>"
100
101
  end
101
-
102
- # In case it was a sublist, close it here
103
- close_last_li_tag_maybe
104
102
  end
103
+ @list_indent_stack.pop
105
104
  end
106
105
 
107
106
  def flush!
108
- if buffer_mode_is_src_block?
109
-
110
- # Only try to colorize #+BEGIN_SRC blocks with a specified language,
111
- # but we still have to catch the cases when a lexer for the language was not available
112
- if defined? Pygments or defined? CodeRay
113
- lang = normalize_lang(@block_lang)
107
+ return false if @buffer.empty?
108
+ case
109
+ when preserve_whitespace?
110
+ strip_code_block! if mode_is_code? current_mode
111
+
112
+ # NOTE: CodeRay and Pygments already escape the html once, so
113
+ # no need to escape_string!(@buffer)
114
+ case
115
+ when (current_mode == :src and defined? Pygments)
116
+ lang = normalize_lang @block_lang
117
+ @output << "\n" unless @new_paragraph == :start
118
+
119
+ begin
120
+ @buffer = Pygments.highlight(@buffer, :lexer => lang)
121
+ rescue
122
+ # Not supported lexer from Pygments, we fallback on using the text lexer
123
+ @buffer = Pygments.highlight(@buffer, :lexer => 'text')
124
+ end
125
+ when (current_mode == :src and defined? CodeRay)
126
+ lang = normalize_lang @block_lang
114
127
 
115
- # NOTE: CodeRay and Pygments already escape the html once, so no need to escape_buffer!
116
- if defined? Pygments
128
+ # CodeRay might throw a warning when unsupported lang is set,
129
+ # then fallback to using the text lexer
130
+ silence_warnings do
117
131
  begin
118
- @buffer = Pygments.highlight(@buffer, :lexer => lang)
119
- rescue
120
- # Not supported lexer from Pygments, we fallback on using the text lexer
121
- @buffer = Pygments.highlight(@buffer, :lexer => 'text')
122
- end
123
- elsif defined? CodeRay
124
- # CodeRay might throw a warning when unsupported lang is set,
125
- # then fallback to using the text lexer
126
- silence_warnings do
127
- begin
128
- @buffer = CodeRay.scan(@buffer, lang).html(:wrap => nil, :css => :style)
129
- rescue ArgumentError
130
- @buffer = CodeRay.scan(@buffer, 'text').html(:wrap => nil, :css => :style)
131
- end
132
+ @buffer = CodeRay.scan(@buffer, lang).html(:wrap => nil, :css => :style)
133
+ rescue ArgumentError
134
+ @buffer = CodeRay.scan(@buffer, 'text').html(:wrap => nil, :css => :style)
132
135
  end
133
136
  end
137
+ when (current_mode == :html or current_mode == :raw_text)
138
+ @buffer.gsub!(/\A\n/, "") if @new_paragraph == :start
139
+ @new_paragraph = true
134
140
  else
135
- escape_buffer!
141
+ escape_string! @buffer
136
142
  end
137
143
 
138
- @logger.debug "FLUSH SRC CODE ==========> #{@buffer.inspect}"
144
+ # Whitespace is significant in :code mode. Always output the
145
+ # buffer and do not do any additional translation.
146
+ @logger.debug "FLUSH CODE ==========> #{@buffer.inspect}"
139
147
  @output << @buffer
140
- elsif mode_is_code(@buffer_mode) then
141
- escape_buffer!
142
148
 
143
- # Whitespace is significant in :code mode. Always output the buffer
144
- # and do not do any additional translation.
145
- @logger.debug "FLUSH CODE ==========> #{@buffer.inspect}"
146
- @output << @buffer << "\n"
149
+ when (mode_is_table? current_mode and skip_tables?)
150
+ @logger.debug "SKIP ==========> #{current_mode}"
151
+
147
152
  else
148
- escape_buffer!
149
- if @buffer.length > 0 and @output_type == :horizontal_rule then
150
- @output << "<hr />\n"
151
- elsif @buffer.length > 0 and @output_type == :definition_list then
152
- unless buffer_mode_is_table? and skip_tables?
153
- output_indentation
154
- d = @buffer.split("::", 2)
155
- @output << "<#{HtmlBlockTag[:definition_term]}#{@title_decoration}>" << inline_formatting(d[0].strip) \
156
- << "</#{HtmlBlockTag[:definition_term]}>"
157
- if d.length > 1 then
158
- @output << "<#{HtmlBlockTag[:definition_descr]}#{@title_decoration}>" << inline_formatting(d[1].strip) \
159
- << "</#{HtmlBlockTag[:definition_descr]}>\n"
160
- else
161
- @output << "\n"
162
- end
163
- @title_decoration = ""
164
- end
165
- elsif @buffer.length > 0 then
166
- unless buffer_mode_is_table? and skip_tables?
167
- @logger.debug "FLUSH ==========> #{@buffer_mode}"
168
- output_indentation
169
- if ((@buffered_lines[0].plain_list?) and
170
- (@unclosed_tags.count == @list_indent_stack.count))
171
- @output << @unclosed_tags.pop
172
- output_indentation
173
- end
174
- @output << "<#{HtmlBlockTag[@output_type]}#{@title_decoration}>"
175
- if (@buffered_lines[0].kind_of?(Headline)) then
176
- headline = @buffered_lines[0]
177
- raise "Cannot be more than one headline!" if @buffered_lines.length > 1
178
- if @options[:export_heading_number] then
179
- level = headline.level
180
- heading_number = get_next_headline_number(level)
181
- output << "<span class=\"heading-number heading-number-#{level}\">#{heading_number} </span>"
182
- end
183
- if @options[:export_todo] and headline.keyword then
184
- keyword = headline.keyword
185
- output << "<span class=\"todo-keyword #{keyword}\">#{keyword} </span>"
186
- end
187
- end
188
- @output << inline_formatting(@buffer)
189
-
190
- # Only close the list when it is the last element from that list,
191
- # or when starting another list
192
- if (@output_type == :unordered_list or
193
- @output_type == :ordered_list or
194
- @output_type == :definition_list) and
195
- (not @list_indent_stack.empty?)
196
- @unclosed_tags.push("</#{HtmlBlockTag[@output_type]}>\n")
197
- @output << "\n"
198
- else
199
- @output << "</#{HtmlBlockTag[@output_type]}>\n"
200
- end
201
- @title_decoration = ""
153
+ @buffer.lstrip!
154
+ @new_paragraph = nil
155
+ @logger.debug "FLUSH ==========> #{current_mode}"
156
+
157
+ case current_mode
158
+ when :definition_term
159
+ d = @buffer.split(/\A(.*[ \t]+|)::(|[ \t]+.*?)$/, 4)
160
+ d[1] = d[1].strip
161
+ unless d[1].empty?
162
+ @output << inline_formatting(d[1])
202
163
  else
203
- @logger.debug "SKIP ==========> #{@buffer_mode}"
164
+ @output << "???"
204
165
  end
166
+ indent = @list_indent_stack.last
167
+ pop_mode
168
+
169
+ @new_paragraph = :start
170
+ push_mode(:definition_descr, indent)
171
+ @output << inline_formatting(d[2].strip + d[3])
172
+ @new_paragraph = nil
173
+
174
+ when :horizontal_rule
175
+ add_paragraph unless @new_paragraph == :start
176
+ @new_paragraph = true
177
+ @output << "<hr />"
178
+
179
+ else
180
+ @output << inline_formatting(@buffer)
205
181
  end
206
182
  end
207
- clear_accumulation_buffer!
183
+ @buffer = ""
184
+ end
185
+
186
+ def add_line_attributes headline
187
+ if @options[:export_heading_number] then
188
+ level = headline.level
189
+ heading_number = get_next_headline_number(level)
190
+ @output << "<span class=\"heading-number heading-number-#{level}\">#{heading_number}</span> "
191
+ end
192
+ if @options[:export_todo] and headline.keyword then
193
+ keyword = headline.keyword
194
+ @output << "<span class=\"todo-keyword #{keyword}\">#{keyword}</span> "
195
+ end
208
196
  end
209
197
 
210
198
  def output_footnotes!
211
199
  return false unless @options[:export_footnotes] and not @footnotes.empty?
212
200
 
213
- @output << "<div id=\"footnotes\">\n<h2 class=\"footnotes\">Footnotes: </h2>\n<div id=\"text-footnotes\">\n"
201
+ @output << "\n<div id=\"footnotes\">\n<h2 class=\"footnotes\">Footnotes:</h2>\n<div id=\"text-footnotes\">\n"
214
202
 
215
203
  @footnotes.each do |name, defi|
216
204
  @output << "<p class=\"footnote\"><sup><a class=\"footnum\" name=\"fn.#{name}\" href=\"#fnr.#{name}\">#{name}</a></sup>" \
@@ -218,11 +206,15 @@ module Orgmode
218
206
  << "</p>\n"
219
207
  end
220
208
 
221
- @output << "</div>\n</div>\n"
209
+ @output << "</div>\n</div>"
222
210
 
223
211
  return true
224
212
  end
225
213
 
214
+ # Test if we're in an output mode in which whitespace is significant.
215
+ def preserve_whitespace?
216
+ super or current_mode == :html
217
+ end
226
218
 
227
219
  ######################################################################
228
220
  private
@@ -231,116 +223,105 @@ module Orgmode
231
223
  @options[:skip_tables]
232
224
  end
233
225
 
234
- def buffer_mode_is_table?
235
- @buffer_mode == :table
226
+ def mode_is_table?(mode)
227
+ (mode == :table or mode == :table_row or
228
+ mode == :table_separator or mode == :table_header)
236
229
  end
237
230
 
238
- def buffer_mode_is_src_block?
239
- @buffer_mode == :src
231
+ # Escapes any HTML content in the output accumulation buffer @buffer.
232
+ def escape_string! str
233
+ str.gsub!(/&/, "&amp;")
234
+ # Escapes the left and right angular brackets but construction
235
+ # @<text> which is formatted to <text>
236
+ str.gsub! /<([^<>\n]*)/ do |match|
237
+ if $`[-1..-1] == "@" and $'[0..0] == ">" then $&
238
+ else "&lt;#{$1}"
239
+ end
240
+ end
241
+ str.gsub! /([^<>\n]*)>/ do |match|
242
+ if $`[-2..-1] == "@<" then $&
243
+ else "#{$1}&gt;"
244
+ end
245
+ end
246
+ str.gsub!(/@(<[^<>\n]*>)/, "\\1")
240
247
  end
241
248
 
242
- # Escapes any HTML content in the output accumulation buffer @buffer.
243
- def escape_buffer!
244
- @buffer.gsub!(/&/, "&amp;")
245
- @buffer.gsub!(/</, "&lt;")
246
- @buffer.gsub!(/>/, "&gt;")
249
+ def buffer_indentation
250
+ indent = " " * @list_indent_stack.length
251
+ @buffer << indent
247
252
  end
248
253
 
249
- def output_indentation
250
- indent = " " * (@mode_stack.length - 1)
251
- @output << indent
254
+ def add_paragraph
255
+ indent = " " * (@list_indent_stack.length - 1)
256
+ @output << "\n" << indent
252
257
  end
253
258
 
254
259
  Tags = {
255
- "*" => { :open => "<b>", :close => "</b>" },
256
- "/" => { :open => "<i>", :close => "</i>" },
257
- "_" => { :open => "<span style=\"text-decoration:underline;\">",
258
- :close => "</span>" },
259
- "=" => { :open => "<code>", :close => "</code>" },
260
- "~" => { :open => "<code>", :close => "</code>" },
261
- "+" => { :open => "<del>", :close => "</del>" }
260
+ "*" => { :open => "b", :close => "b" },
261
+ "/" => { :open => "i", :close => "i" },
262
+ "_" => { :open => "span style=\"text-decoration:underline;\"",
263
+ :close => "span" },
264
+ "=" => { :open => "code", :close => "code" },
265
+ "~" => { :open => "code", :close => "code" },
266
+ "+" => { :open => "del", :close => "del" }
262
267
  }
263
268
 
264
269
  # Applies inline formatting rules to a string.
265
270
  def inline_formatting(str)
266
- str.rstrip!
267
- str = @re_help.rewrite_emphasis(str) do |marker, s|
268
- "#{Tags[marker][:open]}#{s}#{Tags[marker][:close]}"
271
+ @re_help.rewrite_emphasis str do |marker, s|
272
+ "@<#{Tags[marker][:open]}>#{s}@</#{Tags[marker][:close]}>"
269
273
  end
270
274
  if @options[:use_sub_superscripts] then
271
- str = @re_help.rewrite_subp(str) do |type, text|
275
+ @re_help.rewrite_subp str do |type, text|
272
276
  if type == "_" then
273
- "<sub>#{text}</sub>"
277
+ "@<sub>#{text}@</sub>"
274
278
  elsif type == "^" then
275
- "<sup>#{text}</sup>"
279
+ "@<sup>#{text}@</sup>"
276
280
  end
277
281
  end
278
282
  end
279
- str = @re_help.rewrite_images(str) do |link|
280
- "<a href=\"#{link}\"><img src=\"#{link}\" /></a>"
281
- end
282
- str = @re_help.rewrite_links(str) do |link, text|
283
- text ||= link
284
- link = link.sub(/^file:(.*)::(.*?)$/) do
285
-
283
+ @re_help.rewrite_links str do |link, defi|
284
+ [link, defi].compact.each do |text|
286
285
  # We don't support search links right now. Get rid of it.
287
-
288
- "file:#{$1}"
289
- end
290
- if link.match(/^file:.*\.org$/)
291
- link = link.sub(/\.org$/i, ".html")
286
+ text.sub!(/\A(file:[^\s]+)::[^\s]*?\Z/, "\\1")
287
+ text.sub!(/\A(file:[^\s]+)\.org\Z/i, "\\1.html")
288
+ text.sub!(/\Afile:(?=[^\s]+\Z)/, "")
292
289
  end
293
290
 
294
- link = link.sub(/^file:/i, "") # will default to HTTP
291
+ # We don't add a description for images in links, because its
292
+ # empty value forces the image to be inlined.
293
+ defi ||= link unless link =~ @re_help.org_image_file_regexp
295
294
 
296
- text = text.gsub(/([^\]]*\.(jpg|jpeg|gif|png))/xi) do |img_link|
297
- "<img src=\"#{img_link}\" />"
295
+ if defi =~ @re_help.org_image_file_regexp
296
+ defi = "@<img src=\"#{defi}\" alt=\"#{defi}\" />"
297
+ end
298
+
299
+ if defi
300
+ "@<a href=\"#{link}\">#{defi}@</a>"
301
+ else
302
+ "@<img src=\"#{link}\" alt=\"#{link}\" />"
298
303
  end
299
- "<a href=\"#{link}\">#{text}</a>"
300
304
  end
301
- if (@output_type == :table_row) then
302
- str.gsub!(/^\|\s*/, "<td>")
303
- str.gsub!(/\s*\|$/, "</td>")
304
- str.gsub!(/\s*\|\s*/, "</td><td>")
305
+ if @output_type == :table_row
306
+ str.gsub!(/^\|\s*/, "@<td>")
307
+ str.gsub!(/\s*\|$/, "@</td>")
308
+ str.gsub!(/\s*\|\s*/, "@</td>@<td>")
305
309
  end
306
- if (@output_type == :table_header) then
307
- str.gsub!(/^\|\s*/, "<th>")
308
- str.gsub!(/\s*\|$/, "</th>")
309
- str.gsub!(/\s*\|\s*/, "</th><th>")
310
+ if @output_type == :table_header
311
+ str.gsub!(/^\|\s*/, "@<th>")
312
+ str.gsub!(/\s*\|$/, "@</th>")
313
+ str.gsub!(/\s*\|\s*/, "@</th>@<th>")
310
314
  end
311
315
  if @options[:export_footnotes] then
312
- str = @re_help.rewrite_footnote(str) do |name, defi|
316
+ @re_help.rewrite_footnote str do |name, defi|
313
317
  # TODO escape name for url?
314
318
  @footnotes[name] = defi if defi
315
- "<sup><a class=\"footref\" name=\"fnr.#{name}\" href=\"#fn.#{name}\">#{name}</a></sup>"
316
- end
317
- end
318
- Orgmode.special_symbols_to_html(str)
319
- str
320
- end
321
-
322
- def close_floating_li_tags
323
- unless @final_list_node
324
- unless @unclosed_tags.empty?
325
- @output << " " << @unclosed_tags.pop
326
- output_indentation
327
- end
328
- end
329
-
330
- @final_list_node = false
331
- end
332
-
333
- def close_last_li_tag_maybe
334
- if (@list_indent_stack.count < @unclosed_tags.count) and not
335
- (@list_indent_stack.empty? and @unclosed_tags.empty?)
336
- output_indentation
337
- @output << @unclosed_tags.pop
338
- if (@list_indent_stack.count == @unclosed_tags.count) and not
339
- (@list_indent_stack.empty? and @unclosed_tags.empty?)
340
- @final_list_node = true
341
- pop_mode
319
+ "@<sup>@<a class=\"footref\" name=\"fnr.#{name}\" href=\"#fn.#{name}\">#{name}@</a>@</sup>"
342
320
  end
343
321
  end
322
+ escape_string! str
323
+ Orgmode.special_symbols_to_html str
324
+ str = @re_help.restore_code_snippets str
344
325
  end
345
326
 
346
327
  def normalize_lang(lang)
@@ -363,5 +344,11 @@ module Orgmode
363
344
  ensure
364
345
  $VERBOSE = warn_level
365
346
  end
347
+
348
+ def strip_code_block!
349
+ strip_regexp = Regexp.new("^" + " " * @code_block_indent)
350
+ @buffer.gsub!(strip_regexp, "")
351
+ @code_block_indent = nil
352
+ end
366
353
  end # class HtmlOutputBuffer
367
354
  end # module Orgmode