conversio 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY.md +8 -0
- data/README.md +43 -0
- data/bin/conversio +247 -0
- data/conversio.gemspec +56 -0
- data/lib/conversio/converter.rb +141 -0
- data/lib/conversio/htmltoc.rb +115 -0
- data/lib/conversio/pygmentizer.rb +82 -0
- data/lib/conversio.rb +19 -0
- data/templates/dark.erb +296 -0
- data/templates/default.erb +295 -0
- data/templates/light.erb +286 -0
- data/templates/no_css.erb +17 -0
- metadata +113 -0
data/HISTORY.md
ADDED
data/README.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
Conversio by Jörg Behrendt and Victor Penso
|
2
|
+
|
3
|
+
# Description
|
4
|
+
|
5
|
+
Renders plain text files with [Markdown][1] syntax to XHTML pages.
|
6
|
+
User can define their own Ruby ERB templates to customize the
|
7
|
+
XHTML page generation. Also the creation of a table of content
|
8
|
+
using the HTML header elements (like `<h1>`) and the syntax
|
9
|
+
high-lighting of code snippets is supported.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Conversio RubyGem:
|
14
|
+
|
15
|
+
gem install conversio
|
16
|
+
|
17
|
+
Syntax high-lighting is done with Pyhton [Pygments][2]:
|
18
|
+
|
19
|
+
easy_install pygments
|
20
|
+
|
21
|
+
## Usage Examples
|
22
|
+
|
23
|
+
Take a look to the help text:
|
24
|
+
|
25
|
+
conversio -h
|
26
|
+
|
27
|
+
Convert all files called `*.markdown` inside a defined directory
|
28
|
+
and all sub-directories into HTML and store the in the destination
|
29
|
+
directory.
|
30
|
+
|
31
|
+
conversio ~/docs/path/to/files ~/public/path
|
32
|
+
|
33
|
+
Create a single `readme.html` file including a table of content by
|
34
|
+
using the 'dark' template:
|
35
|
+
|
36
|
+
conversio -t -p dark readme.markdown
|
37
|
+
|
38
|
+
## License
|
39
|
+
|
40
|
+
GPLv3 - see the COPYING file.
|
41
|
+
|
42
|
+
[1]: http://daringfireball.net/projects/markdown/
|
43
|
+
[2]: http://pygments.org/
|
data/bin/conversio
ADDED
@@ -0,0 +1,247 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$DEBUG = false
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'erb'
|
7
|
+
require 'yaml'
|
8
|
+
require 'fileutils'
|
9
|
+
require 'pathname'
|
10
|
+
require 'getoptlong'
|
11
|
+
require 'ostruct'
|
12
|
+
require 'conversio'
|
13
|
+
|
14
|
+
include Conversio
|
15
|
+
|
16
|
+
help = <<EOF
|
17
|
+
Synopsis
|
18
|
+
--------
|
19
|
+
|
20
|
+
#{File.split(__FILE__)[-1]}: Renders Markdown plain text files to HTML
|
21
|
+
|
22
|
+
Purpose
|
23
|
+
-------
|
24
|
+
|
25
|
+
Uses Ruby ERB Templates to generate XHTML documents rendered from Markdown
|
26
|
+
plain text files.
|
27
|
+
|
28
|
+
Usage
|
29
|
+
-----
|
30
|
+
|
31
|
+
#{File.split(__FILE__)[-1]} [OPTIONS] SRC [DST]
|
32
|
+
|
33
|
+
SRC: File or directory containing the Markdown formated plain text
|
34
|
+
DST: Target directory for the XHTML output.
|
35
|
+
|
36
|
+
Options
|
37
|
+
-------
|
38
|
+
|
39
|
+
--config:
|
40
|
+
|
41
|
+
Creates a personal configuration file in ~/.conversiorc
|
42
|
+
|
43
|
+
-c, --colorize:
|
44
|
+
|
45
|
+
Enable syntax high-lighting for marked code blocks.
|
46
|
+
|
47
|
+
-e, --engine:
|
48
|
+
|
49
|
+
Select the Markdown parser to be used:
|
50
|
+
* 'bluecloth' (default)
|
51
|
+
* 'kramdown'
|
52
|
+
|
53
|
+
-f, --template-file FILE:
|
54
|
+
|
55
|
+
FILE containing an ERB template with:
|
56
|
+
* '<%= content %>' to mark the postion inside the body tag
|
57
|
+
to place the passed in content.
|
58
|
+
* '<%= style %>' to mark the position for placing CSS.
|
59
|
+
|
60
|
+
-h, --help:
|
61
|
+
|
62
|
+
Show this help documentation.
|
63
|
+
|
64
|
+
-i, --ignore-config:
|
65
|
+
|
66
|
+
Don't read the configuration from ~/.conversiorc
|
67
|
+
|
68
|
+
-l, --list-templates:
|
69
|
+
|
70
|
+
Show a list of all available templates.
|
71
|
+
|
72
|
+
-t, --table-of-content:
|
73
|
+
|
74
|
+
Enables the creation of a table of content.
|
75
|
+
|
76
|
+
-p, --template NAME:
|
77
|
+
|
78
|
+
Select a specific template to be used.
|
79
|
+
|
80
|
+
-v, --verbose:
|
81
|
+
|
82
|
+
Print more verbose output.
|
83
|
+
EOF
|
84
|
+
|
85
|
+
config = <<EOF
|
86
|
+
colorize: true
|
87
|
+
table_of_content: true
|
88
|
+
template_file:
|
89
|
+
engine:
|
90
|
+
EOF
|
91
|
+
|
92
|
+
# -------------------------------------------------------------
|
93
|
+
# helper functions
|
94
|
+
# -------------------------------------------------------------
|
95
|
+
|
96
|
+
def ask?(*question)
|
97
|
+
print question.join(' '), '? (y/n) > '
|
98
|
+
return true if STDIN.gets.chomp.downcase.eql?('y')
|
99
|
+
return false
|
100
|
+
end
|
101
|
+
|
102
|
+
def overwrite?(*str)
|
103
|
+
return ask?('Overwrite',str)
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
# -------------------------------------------------------------
|
108
|
+
# class extensions
|
109
|
+
# -------------------------------------------------------------
|
110
|
+
|
111
|
+
class Array
|
112
|
+
def resolv_path
|
113
|
+
Hash[ *self.collect { |e| [e,e.gsub(/.markdown/,'.html') ] }.flatten ]
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# -------------------------------------------------------------
|
118
|
+
# main program
|
119
|
+
# -------------------------------------------------------------
|
120
|
+
begin
|
121
|
+
|
122
|
+
user_config = "#{ENV['HOME']}/.conversiorc"
|
123
|
+
|
124
|
+
|
125
|
+
# defaults for program arguments
|
126
|
+
options = OpenStruct.new
|
127
|
+
options.ignore_config = false
|
128
|
+
options.colorize = false
|
129
|
+
options.engine = nil
|
130
|
+
options.template_file = nil
|
131
|
+
options.table_of_content = false
|
132
|
+
options.verbose = false
|
133
|
+
options.template = 'default'
|
134
|
+
|
135
|
+
# list of user options
|
136
|
+
opts = GetoptLong.new(
|
137
|
+
[ '--colorize', '-c', GetoptLong::NO_ARGUMENT],
|
138
|
+
[ '--config', GetoptLong::NO_ARGUMENT],
|
139
|
+
[ '--engine', '-e', GetoptLong::OPTIONAL_ARGUMENT],
|
140
|
+
[ '--ignore-config', '-i', GetoptLong::NO_ARGUMENT],
|
141
|
+
[ '--help', '-h', GetoptLong::NO_ARGUMENT ],
|
142
|
+
[ '--list-templates', '-l', GetoptLong::NO_ARGUMENT ],
|
143
|
+
[ '--table-of-content', '-t', GetoptLong::NO_ARGUMENT],
|
144
|
+
[ '--template-file', '-f', GetoptLong::OPTIONAL_ARGUMENT ],
|
145
|
+
[ '--template', '-p', GetoptLong::OPTIONAL_ARGUMENT ],
|
146
|
+
[ '--verbose', '-v', GetoptLong::NO_ARGUMENT]
|
147
|
+
)
|
148
|
+
|
149
|
+
# parse the options from the command line
|
150
|
+
opts.each do |opt, arg|
|
151
|
+
case opt
|
152
|
+
when '--colorize'
|
153
|
+
options.colorize = true
|
154
|
+
when '--config'
|
155
|
+
open(user_config,'w') { |f| f.write config } if overwrite? user_config
|
156
|
+
exit 0
|
157
|
+
when '--engine'
|
158
|
+
options.engine = arg
|
159
|
+
when '--ignore-config'
|
160
|
+
options.ignore_config = true
|
161
|
+
when '--help'
|
162
|
+
puts help
|
163
|
+
exit 0
|
164
|
+
when '--list-templates'
|
165
|
+
Template.constants.each { |tpl| puts tpl.downcase }
|
166
|
+
exit 0
|
167
|
+
when '--table-of-content'
|
168
|
+
options.table_of_content = true
|
169
|
+
when '--template-file'
|
170
|
+
options.template_file = arg
|
171
|
+
when '--template'
|
172
|
+
options.template = arg
|
173
|
+
when '--verbose'
|
174
|
+
options.verbose = true
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
# get the input source
|
180
|
+
src = ARGV[0] || raise("no input defined")
|
181
|
+
dst = ARGV[1] # optional parameter!
|
182
|
+
|
183
|
+
# read the default configuration of the user
|
184
|
+
if not options.ignore_config and File.exists? user_config then
|
185
|
+
STDERR.puts "Reading configuration file: #{user_config}" if options.verbose
|
186
|
+
defaults = YAML.load_file user_config
|
187
|
+
# command-line arguments have precedents before the suer configuration
|
188
|
+
options.colorize = defaults['colorize']
|
189
|
+
options.engine = defaults['engine'] if options.engine.nil?
|
190
|
+
options.table_of_content = defaults['table_of_content']
|
191
|
+
options.template_file = defaults['template_file'] if options.template_file.nil?
|
192
|
+
end
|
193
|
+
|
194
|
+
STDERR.print 'Configuration: ', options, "\n" if options.verbose
|
195
|
+
|
196
|
+
template = String.new
|
197
|
+
# user the default ERB template if the user hasn't defined its own
|
198
|
+
if options.template_file.nil? then
|
199
|
+
STDERR.print 'Using ERB template: ', options.template, "\n" if options.verbose
|
200
|
+
template = Template.const_get(options.template.upcase)
|
201
|
+
else
|
202
|
+
STDERR.print 'Using ERB template: ', options.template_file, "\n" if options.verbose
|
203
|
+
options.template_file = File.expand_path(options.template_file)
|
204
|
+
if File.exists? options.template_file
|
205
|
+
template = File.read options.template_file
|
206
|
+
else
|
207
|
+
raise("The specified ERB templates is not existing!")
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
# setup the converter object
|
212
|
+
converter = Converter.new(template)
|
213
|
+
converter.load_parser(options.engine) unless options.engine.nil?
|
214
|
+
converter.color = true if options.colorize
|
215
|
+
converter.table_of_content = true if options.table_of_content
|
216
|
+
|
217
|
+
# get all the input files
|
218
|
+
input_files = Array.new
|
219
|
+
if File.directory?(src) then
|
220
|
+
input_files = Dir["#{src}/**/*.markdown"]
|
221
|
+
else
|
222
|
+
file = File.expand_path(src)
|
223
|
+
input_files << file
|
224
|
+
src = File.dirname(file)
|
225
|
+
end
|
226
|
+
src_dst_pairs = input_files.resolv_path
|
227
|
+
# fix the destination path if needed
|
228
|
+
unless dst.nil? then
|
229
|
+
src_dst_pairs.each_pair do |src_path,dst_path|
|
230
|
+
src_dst_pairs[src_path] = dst_path.gsub(/#{src}/,dst)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
# render the XHTML docs
|
234
|
+
STDERR.puts 'Created files:' if options.verbose
|
235
|
+
src_dst_pairs.each_pair do |s,d|
|
236
|
+
converter.markdown_to_xhtml(s,d)
|
237
|
+
STDERR.print ' ', d, "\n" if options.verbose
|
238
|
+
end
|
239
|
+
|
240
|
+
exit 0
|
241
|
+
|
242
|
+
rescue => exc
|
243
|
+
STDERR.puts "ERROR: #{exc.message}"
|
244
|
+
STDERR.puts " use -h for detailed instructions"
|
245
|
+
exit 1
|
246
|
+
end
|
247
|
+
|
data/conversio.gemspec
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
|
5
|
+
s.name = %q{conversio}
|
6
|
+
s.version = "0.1.1"
|
7
|
+
|
8
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
9
|
+
s.authors = ["Jörg Behrendt","Victor Penso"]
|
10
|
+
s.date = %q{2010-11-19}
|
11
|
+
s.default_executable = %q{conversio}
|
12
|
+
s.homepage = 'https://github.com/vpenso/conversio'
|
13
|
+
|
14
|
+
s.description = <<-EOF
|
15
|
+
Renders plain text files with Markdown syntax to XHTML pages.
|
16
|
+
User can define their own Ruby ERB templates to customize the
|
17
|
+
XHTML page generation. Also the creation of a table of content
|
18
|
+
using the HTML header elements (like `<h1>`) and the syntax
|
19
|
+
high-lighting of code snippets is supported.
|
20
|
+
EOF
|
21
|
+
|
22
|
+
s.email = %q{v.penso@gsi.de}
|
23
|
+
s.executables = ["conversio"]
|
24
|
+
s.extra_rdoc_files = [
|
25
|
+
"bin/conversio",
|
26
|
+
"lib/conversio.rb",
|
27
|
+
"lib/conversio/converter.rb",
|
28
|
+
"lib/conversio/htmltoc.rb",
|
29
|
+
"lib/conversio/pygmentizer.rb"
|
30
|
+
]
|
31
|
+
s.files = [
|
32
|
+
"README.md",
|
33
|
+
"HISTORY.md",
|
34
|
+
"bin/conversio",
|
35
|
+
"conversio.gemspec",
|
36
|
+
"lib/conversio.rb",
|
37
|
+
"lib/conversio/converter.rb",
|
38
|
+
"lib/conversio/htmltoc.rb",
|
39
|
+
"lib/conversio/pygmentizer.rb",
|
40
|
+
"templates/default.erb",
|
41
|
+
"templates/no_css.erb",
|
42
|
+
"templates/dark.erb",
|
43
|
+
"templates/light.erb"
|
44
|
+
]
|
45
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Conversio"]
|
46
|
+
s.require_paths = ["lib"]
|
47
|
+
s.rubyforge_project = %q{conversio}
|
48
|
+
s.rubygems_version = %q{1.3.6}
|
49
|
+
s.summary = %q{Renders Markdown plain text files to HTML}
|
50
|
+
|
51
|
+
s.add_dependency('bluecloth', '>= 2.0.9')
|
52
|
+
s.add_dependency('kramdown')
|
53
|
+
s.requirements << 'Pygments (http://pygments.org/)'
|
54
|
+
s.licenses = 'GPLv3'
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Conversio
|
4
|
+
|
5
|
+
class Hash
|
6
|
+
|
7
|
+
def deep_merge(hash)
|
8
|
+
target = dup
|
9
|
+
|
10
|
+
hash.keys.each do |key|
|
11
|
+
if hash[key].is_a? Hash and self[key].is_a? Hash
|
12
|
+
target[key] = target[key].deep_merge(hash[key])
|
13
|
+
next
|
14
|
+
end
|
15
|
+
|
16
|
+
target[key] = hash[key]
|
17
|
+
end
|
18
|
+
|
19
|
+
target
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def deep_merge!(second)
|
24
|
+
second.each_pair do |k,v|
|
25
|
+
if self[k].is_a?(Hash) and second[k].is_a?(Hash)
|
26
|
+
self[k].deep_merge!(second[k])
|
27
|
+
else
|
28
|
+
self[k] = second[k]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
class Converter
|
36
|
+
|
37
|
+
attr_accessor :table_of_content, :color
|
38
|
+
|
39
|
+
def initialize(template)
|
40
|
+
@template = template
|
41
|
+
@table_of_content = false
|
42
|
+
@color = false
|
43
|
+
#user_config = "#{ENV['HOME']}/.conversiorc"
|
44
|
+
#if File.exists?(user_config)
|
45
|
+
# overwrite defaults
|
46
|
+
#@meta_data = @meta_data.deep_merge(YAML.load_file(user_config))
|
47
|
+
#end
|
48
|
+
# Holds the input Markdown plain text
|
49
|
+
@source = nil
|
50
|
+
# Hold Markdown rendered to HTML
|
51
|
+
@content = nil
|
52
|
+
# Hold the finished XHTML document
|
53
|
+
@result = nil
|
54
|
+
# load the default parser
|
55
|
+
@parser = 'bluecloth'
|
56
|
+
load_parser(@parser)
|
57
|
+
end
|
58
|
+
|
59
|
+
def markdown_to_xhtml(src,dst)
|
60
|
+
@source = open(src).readlines.join
|
61
|
+
colorize() if @color
|
62
|
+
parse()
|
63
|
+
generate_table_of_content() if @table_of_content
|
64
|
+
render()
|
65
|
+
# write the HTML file
|
66
|
+
FileUtils::mkdir_p(File.dirname(dst)) unless File.exists?(File.dirname(dst))
|
67
|
+
open(dst,'w') { |f| f.write @result }
|
68
|
+
end
|
69
|
+
|
70
|
+
def load_parser(parser)
|
71
|
+
begin
|
72
|
+
case parser
|
73
|
+
when 'bluecloth'
|
74
|
+
require 'bluecloth'
|
75
|
+
when 'kramdown'
|
76
|
+
require 'kramdown'
|
77
|
+
else
|
78
|
+
raise "Parser '#{parser}' is not a known Markdown library"
|
79
|
+
end
|
80
|
+
rescue LoadError
|
81
|
+
raise "Couldn't load #{parser}."
|
82
|
+
end
|
83
|
+
@parser = parser
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def configure()
|
89
|
+
config = nil
|
90
|
+
# read the header of the source file
|
91
|
+
start = @source.index("|--")
|
92
|
+
ende = @source.index("--|")
|
93
|
+
#if start != nil and ende != nil then
|
94
|
+
if false
|
95
|
+
STDERR.puts 'Meta data found in file!' if $DEBUG
|
96
|
+
yamlheader = @source[start+3,ende-start-3]
|
97
|
+
# overwrite defaults
|
98
|
+
config = @meta_data.deep_merge(YAML.load(yamlheader))
|
99
|
+
splitted = @source.split('--|',2)
|
100
|
+
@source = splitted[1]
|
101
|
+
else
|
102
|
+
config = @meta_data
|
103
|
+
end
|
104
|
+
return config
|
105
|
+
end
|
106
|
+
|
107
|
+
def load_template(tpl)
|
108
|
+
puts "Loading template : "+tpl.to_s
|
109
|
+
raise "Couldn't open ERB template: #{tpl}" unless File.exists?(File.expand_path(tpl))
|
110
|
+
return open(File.expand_path(tpl)).readlines.join
|
111
|
+
end
|
112
|
+
|
113
|
+
def parse
|
114
|
+
raise "Define source before rendering!" if @source == nil
|
115
|
+
case @parser
|
116
|
+
when 'bluecloth'
|
117
|
+
@content = BlueCloth::new(@source).to_html
|
118
|
+
when 'kramdown'
|
119
|
+
@content = Kramdown::Document.new(@source).to_html
|
120
|
+
else
|
121
|
+
puts "Markdown parser #{@parser} not supported yet"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def colorize
|
126
|
+
@source = Pygmentizer.new.transform_code_blocks(@source)
|
127
|
+
end
|
128
|
+
|
129
|
+
def generate_table_of_content #füge zum geparsten Text ein Inhaltsverzeichnis hinzu -> davor parsen
|
130
|
+
raise "No content to generate table of content - Run the parser first!" if @content == nil
|
131
|
+
@content = HTMLTableOfContent.new(@content).get_html()
|
132
|
+
end
|
133
|
+
|
134
|
+
def render(values = {})
|
135
|
+
values.store(:content, @content)
|
136
|
+
@result = ERB.new(@template).result(binding)
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module Conversio
|
2
|
+
|
3
|
+
class HTMLTableOfContent
|
4
|
+
|
5
|
+
attr_accessor :numbering, :table_of_content_div_class
|
6
|
+
|
7
|
+
def initialize(html_input)
|
8
|
+
@numbering = true
|
9
|
+
@table_of_content_div_class = 'toc'
|
10
|
+
# Variables
|
11
|
+
@html_input = Array.new
|
12
|
+
@heading_elements = Array.new
|
13
|
+
html_input.split("\n").each { |l| @html_input << l }
|
14
|
+
scan_for_heading_elements()
|
15
|
+
numbers()
|
16
|
+
end
|
17
|
+
|
18
|
+
def get_html_with_anchors()
|
19
|
+
inject_anchors()
|
20
|
+
return @html_input.join("\n")
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_html_table_of_content()
|
24
|
+
output = String.new
|
25
|
+
@heading_elements.each do |heading|
|
26
|
+
index, level, content, anchor, number = heading
|
27
|
+
level = level.to_i
|
28
|
+
next if level > 3 # only h1,h2, and h3 tags are used
|
29
|
+
space = ' '
|
30
|
+
case level
|
31
|
+
when 2 then output << space
|
32
|
+
when 3 then output << space << space
|
33
|
+
end
|
34
|
+
content = "#{number} #{content}" if numbering?
|
35
|
+
output << %{<a href="##{anchor}">#{content}</a><br/>\n}
|
36
|
+
end
|
37
|
+
return %{<div class="#{@table_of_content_div_class}">\n#{output}</div>\n}
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_html()
|
41
|
+
return get_html_table_of_content() << "\n" << get_html_with_anchors
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
def numbering?
|
47
|
+
return @numbering
|
48
|
+
end
|
49
|
+
|
50
|
+
def scan_for_heading_elements()
|
51
|
+
@html_input.each_index do |index|
|
52
|
+
if @html_input[index] =~ %r{<h(\d)(.*?)>(.*?)</h\1>$}m then
|
53
|
+
# Pattern match values:
|
54
|
+
# $1 -- header tag level, e.g. <h2>...</h2> will be 2
|
55
|
+
# $3 -- content between the tags
|
56
|
+
@heading_elements << [index, $1, $3, anchor($3)]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Transforms the input string into a valid XHTML anchor (ID attribute).
|
62
|
+
#
|
63
|
+
# anchor("Text with spaces") # textwithspaces
|
64
|
+
# anchor("step 1 step 2 step: 3") # step1step2step3
|
65
|
+
def anchor(string)
|
66
|
+
alnum = String.new
|
67
|
+
string.gsub(/[[:alnum:]]/) { |c| alnum << c }
|
68
|
+
return alnum.downcase
|
69
|
+
end
|
70
|
+
|
71
|
+
def numbers()
|
72
|
+
chapters = 0
|
73
|
+
sections = 0
|
74
|
+
subsections = 0
|
75
|
+
@heading_elements.each_index do |index|
|
76
|
+
level = @heading_elements[index][1].to_i
|
77
|
+
case level
|
78
|
+
when 1
|
79
|
+
chapters = chapters.next
|
80
|
+
@heading_elements[index] << "#{chapters}"
|
81
|
+
sections = 0
|
82
|
+
subsections = 0
|
83
|
+
when 2
|
84
|
+
sections = sections.next
|
85
|
+
@heading_elements[index] << "#{chapters}.#{sections}"
|
86
|
+
subsections = 0
|
87
|
+
when 3
|
88
|
+
subsections = subsections.next
|
89
|
+
@heading_elements[index] << "#{chapters}.#{sections}.#{subsections}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def inject_anchors()
|
95
|
+
@heading_elements.each do |heading|
|
96
|
+
line = String.new
|
97
|
+
index = heading[0]
|
98
|
+
level = heading[1].to_i
|
99
|
+
content = heading[2]
|
100
|
+
anchor = heading[3]
|
101
|
+
next if level > 3 # only h1,h2, and h3 tags are used
|
102
|
+
if numbering? then
|
103
|
+
number = heading[4]
|
104
|
+
line = %{<h#{level}><a name="#{anchor}"></a>#{number} #{content}</h#{level}>}
|
105
|
+
else
|
106
|
+
line = %{<h#{level}><a name="#{anchor}"></a>#{content}</h#{level}>}
|
107
|
+
end
|
108
|
+
@html_input[index] = line
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Conversio
|
2
|
+
|
3
|
+
|
4
|
+
class Pygmentizer
|
5
|
+
|
6
|
+
def self.respond_to?( command )
|
7
|
+
return true if `which #{command}`.empty?
|
8
|
+
return false
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.run(command, input='')
|
12
|
+
puts command if $DEBUG
|
13
|
+
IO.popen(command, 'r+') do |io|
|
14
|
+
io.puts input
|
15
|
+
io.close_write
|
16
|
+
return io.read
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
def output(string)
|
23
|
+
@output << "#{string}\n"
|
24
|
+
end
|
25
|
+
|
26
|
+
def transform_code_blocks(text)
|
27
|
+
raise RuntimeError, "pygmentize not in path" if
|
28
|
+
Pygmentizer::respond_to?("pygmentize")
|
29
|
+
@input_by_line = Array.new
|
30
|
+
text.each_line { |line| @input_by_line << line.chop }
|
31
|
+
@output = String.new
|
32
|
+
buffer = Array.new
|
33
|
+
rec = false
|
34
|
+
@input_by_line.each do |line|
|
35
|
+
# true if a Markdown code block is found
|
36
|
+
rec = true if !rec and line =~ /^ /
|
37
|
+
# store the code block into buffer
|
38
|
+
if rec and line =~ /^ / then
|
39
|
+
# remove the leading 4 spaces
|
40
|
+
line = line.gsub(/^ /,'')
|
41
|
+
buffer << line
|
42
|
+
# End of code block
|
43
|
+
elsif rec
|
44
|
+
block_to_html(buffer)
|
45
|
+
# Wipe the buffer
|
46
|
+
buffer.clear
|
47
|
+
rec = false
|
48
|
+
# Anyting beside code blocks will be output
|
49
|
+
else
|
50
|
+
output(line)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
return @output
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
def block_to_html(block)
|
58
|
+
style = get_style(block[0])
|
59
|
+
unless style.empty? then
|
60
|
+
# remove the style information from the code block
|
61
|
+
block.slice!(0)
|
62
|
+
output(highlight(block.join("\n"),style))
|
63
|
+
else
|
64
|
+
# Code blocks without style information will be
|
65
|
+
# put to output includeing the 4 leading spaces
|
66
|
+
block.each {|line| output(" #{line}") }
|
67
|
+
output("")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def get_style(string)
|
73
|
+
return string.gsub(/--/,'').strip if string =~ /^--[a-z]* */
|
74
|
+
return ""
|
75
|
+
end
|
76
|
+
|
77
|
+
def highlight(string, style)
|
78
|
+
return Pygmentizer::run("pygmentize -f html -l #{style}", string) << "\n"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
data/lib/conversio.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'conversio/converter'
|
2
|
+
require 'conversio/pygmentizer'
|
3
|
+
require 'conversio/htmltoc'
|
4
|
+
|
5
|
+
module Conversio
|
6
|
+
class Template
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# find the template directory
|
11
|
+
templates = File.join(File.dirname(File.expand_path(__FILE__)),'..','templates')
|
12
|
+
# find the ERB templates
|
13
|
+
Dir.glob("#{templates}/*.erb") do |template|
|
14
|
+
# add the template as constant to the class Templates
|
15
|
+
Conversio::Template.const_set(
|
16
|
+
File.basename(template,".erb").upcase,
|
17
|
+
File.read(template))
|
18
|
+
end
|
19
|
+
|