conversio 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|