transmuter 0.0.0.1 → 0.0.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/.gitignore +2 -0
- data/.travis.yml +8 -0
- data/Gemfile +17 -0
- data/Guardfile +13 -0
- data/README.md +35 -1
- data/Rakefile +7 -0
- data/bin/transmute +7 -0
- data/lib/transmuter/cli/thor.rb +99 -0
- data/lib/transmuter/cli/transmute.rb +67 -0
- data/lib/transmuter/cli.rb +13 -0
- data/lib/transmuter/core_ext.rb +4 -0
- data/lib/transmuter/format/html.rb +81 -0
- data/lib/transmuter/format/markdown.rb +45 -0
- data/lib/transmuter/format/pdf.rb +25 -0
- data/lib/transmuter/format.rb +9 -0
- data/lib/transmuter/version.rb +3 -4
- data/lib/transmuter.rb +5 -4
- data/spec/spec_helper.rb +25 -0
- data/spec/transmuter/cli/thor_spec.rb +175 -0
- data/spec/transmuter/cli/transmute_spec.rb +138 -0
- data/spec/transmuter/cli_spec.rb +4 -0
- data/spec/transmuter/format/html_spec.rb +159 -0
- data/spec/transmuter/format/markdown_spec.rb +150 -0
- data/spec/transmuter/format/pdf_spec.rb +49 -0
- data/spec/transmuter/format_spec.rb +4 -0
- data/stylesheets/default.css +67 -0
- data/transmuter.gemspec +26 -3
- metadata +180 -8
data/.travis.yml
ADDED
data/Gemfile
CHANGED
@@ -2,3 +2,20 @@ source "http://rubygems.org"
|
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in transmuter.gemspec
|
4
4
|
gemspec
|
5
|
+
|
6
|
+
platforms :ruby do
|
7
|
+
# Require rbconfig to figure out the target OS
|
8
|
+
require 'rbconfig'
|
9
|
+
|
10
|
+
unless ENV['TRAVIS']
|
11
|
+
if RbConfig::CONFIG['target_os'] =~ /darwin/i
|
12
|
+
gem 'rb-fsevent', require: false
|
13
|
+
gem 'ruby-growl', require: false
|
14
|
+
gem 'growl', require: false
|
15
|
+
end
|
16
|
+
if RbConfig::CONFIG['target_os'] =~ /linux/i
|
17
|
+
gem 'rb-inotify', require: false
|
18
|
+
gem 'libnotify', require: false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/Guardfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard 'bundler' do
|
5
|
+
watch('Gemfile')
|
6
|
+
watch(/^.+\.gemspec/)
|
7
|
+
end
|
8
|
+
|
9
|
+
guard 'rspec', :version => 2 do
|
10
|
+
watch(%r{^spec/.+_spec\.rb$})
|
11
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
12
|
+
watch('spec/spec_helper.rb') { "spec" }
|
13
|
+
end
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Transmuter
|
1
|
+
# Transmuter [](http://travis-ci.org/TechnoGate/transmuter)
|
2
2
|
|
3
3
|
Transmuter is a command line tool to convert Markdown files into HTML or PDF
|
4
4
|
files, it can also be used to convert HTML files to PDF, it uses in the
|
@@ -6,6 +6,8 @@ backgound [Redcarper](https://github.com/tanoku/redcarpet),
|
|
6
6
|
[Albino](https://github.com/github/albino) and
|
7
7
|
[PDFkit](https://github.com/jdpace/PDFKit).
|
8
8
|
|
9
|
+
<a href='http://www.pledgie.com/campaigns/16086'><img alt='Click here to lend your support to: Transmuter and make a donation at www.pledgie.com !' src='http://www.pledgie.com/campaigns/16086.png?skin_name=chrome' border='0' /></a>
|
10
|
+
|
9
11
|
# Installation
|
10
12
|
|
11
13
|
To install Transmuter use the command
|
@@ -35,3 +37,35 @@ $ sudo easy_install pygments
|
|
35
37
|
2. Try using the wkhtmltopdf-binary gem (mac + linux i386)
|
36
38
|
|
37
39
|
gem install wkhtmltopdf-binary
|
40
|
+
|
41
|
+
# Usage
|
42
|
+
|
43
|
+
You should check the help
|
44
|
+
|
45
|
+
```bash
|
46
|
+
$ transmute --help
|
47
|
+
```
|
48
|
+
|
49
|
+
To Generate a PDF from a markdown file with the default CSS:
|
50
|
+
|
51
|
+
```bash
|
52
|
+
$ transmute file.md
|
53
|
+
```
|
54
|
+
|
55
|
+
To Generate an HTML from a markdown file with the default CSS:
|
56
|
+
|
57
|
+
```bash
|
58
|
+
$ transmute file.md -t html
|
59
|
+
```
|
60
|
+
|
61
|
+
To Generate an HTML from a markdown file with custom CSS:
|
62
|
+
|
63
|
+
```bash
|
64
|
+
$ transmute file.md -t html -s custom.css
|
65
|
+
```
|
66
|
+
|
67
|
+
Custom CSS files can be be multiple path separated by a space, for example:
|
68
|
+
|
69
|
+
```bash
|
70
|
+
$ transmute file.md -t html -s custom1.css custom2.css
|
71
|
+
```
|
data/Rakefile
CHANGED
data/bin/transmute
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'thor/group'
|
3
|
+
|
4
|
+
module Transmuter
|
5
|
+
module CLI
|
6
|
+
module Thor
|
7
|
+
|
8
|
+
def self.included(base)
|
9
|
+
base.send :include, InstanceMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
module InstanceMethods
|
13
|
+
def self.included(base)
|
14
|
+
base.class_eval <<-END, __FILE__, __LINE__ + 1
|
15
|
+
desc "Transmute one file format into another."
|
16
|
+
|
17
|
+
class_option :input_format,
|
18
|
+
type: :string,
|
19
|
+
required: false,
|
20
|
+
aliases: "-f",
|
21
|
+
desc: "The input format."
|
22
|
+
|
23
|
+
class_option :output_format,
|
24
|
+
type: :string,
|
25
|
+
required: false,
|
26
|
+
aliases: "-t",
|
27
|
+
default: "pdf",
|
28
|
+
desc: "The output format."
|
29
|
+
|
30
|
+
class_option :stylesheets,
|
31
|
+
type: :array,
|
32
|
+
required: false,
|
33
|
+
aliases: "-s",
|
34
|
+
default: [DEFAULT_THEME],
|
35
|
+
desc: "The stylesheets."
|
36
|
+
|
37
|
+
argument :input,
|
38
|
+
type: :string,
|
39
|
+
required: true,
|
40
|
+
aliases: "-i",
|
41
|
+
desc: "The input file name."
|
42
|
+
|
43
|
+
argument :output,
|
44
|
+
type: :string,
|
45
|
+
required: false,
|
46
|
+
aliases: "-o",
|
47
|
+
desc: "The output file name."
|
48
|
+
|
49
|
+
def set_input_filename
|
50
|
+
@input_filename = input
|
51
|
+
end
|
52
|
+
|
53
|
+
def set_input_fileformat
|
54
|
+
@input_fileformat = options[:input_format] || input_format
|
55
|
+
end
|
56
|
+
|
57
|
+
def set_output_fileformat
|
58
|
+
@output_fileformat = options[:output_format]
|
59
|
+
end
|
60
|
+
|
61
|
+
def set_output_filename
|
62
|
+
if output.blank? && options[:output_format].blank?
|
63
|
+
raise ArgumentError, "Either output or output_format should be given,"
|
64
|
+
end
|
65
|
+
|
66
|
+
@output_filename = output || output_file
|
67
|
+
end
|
68
|
+
|
69
|
+
def set_stylesheets
|
70
|
+
@stylesheets = options[:stylesheets]
|
71
|
+
end
|
72
|
+
|
73
|
+
def transmute_input_to_output
|
74
|
+
transmute!
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
|
79
|
+
def input_format
|
80
|
+
case @input.split('.').last
|
81
|
+
when /^(md|markdown)$/
|
82
|
+
"markdown"
|
83
|
+
when /^(html|htm)/
|
84
|
+
"html"
|
85
|
+
else
|
86
|
+
raise ArgumentError, "No format was given and format could not be parsed from the file name"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def output_file
|
91
|
+
output = @input_filename.dup
|
92
|
+
output.gsub(/^(.+)\\.[^.]*$/, "\\\\1.\#{@output_fileformat}")
|
93
|
+
end
|
94
|
+
END
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Transmuter
|
2
|
+
module CLI
|
3
|
+
module Transmute
|
4
|
+
def self.included(base)
|
5
|
+
base.send :include, InstanceMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module InstanceMethods
|
9
|
+
def transmute!
|
10
|
+
set_klasses!
|
11
|
+
set_methods
|
12
|
+
verify_klasses!
|
13
|
+
|
14
|
+
source_klass_instance = @source_klass.new(read_input_file, parse_transmute_options)
|
15
|
+
output = source_klass_instance.send(@source_transform_method)
|
16
|
+
|
17
|
+
write_output_file(output)
|
18
|
+
end
|
19
|
+
|
20
|
+
def transmute
|
21
|
+
transmute!
|
22
|
+
rescue Exception => e
|
23
|
+
handle_error(e)
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
def handle_error(exception)
|
28
|
+
# TODO: Handle error properly
|
29
|
+
end
|
30
|
+
|
31
|
+
def set_klasses!
|
32
|
+
@source_klass ||= "::Transmuter::Format::#{@input_fileformat.to_s.camelcase}".constantize
|
33
|
+
@destination_klass ||= "::Transmuter::Format::#{@output_fileformat.to_s.camelcase}".constantize
|
34
|
+
end
|
35
|
+
|
36
|
+
def set_methods
|
37
|
+
@source_transform_method ||= "to_#{@output_fileformat.to_s.downcase}".to_sym
|
38
|
+
@destination_process_method ||= :process
|
39
|
+
end
|
40
|
+
|
41
|
+
def verify_klasses!
|
42
|
+
|
43
|
+
raise NotImplementedError,
|
44
|
+
"#{@source_klass} does not respond to #{@source_transform_method}" unless
|
45
|
+
@source_klass.public_instance_methods.include?(@source_transform_method)
|
46
|
+
|
47
|
+
raise NotImplementedError,
|
48
|
+
"#{@destination_klass} does not respond to #{@destination_process_method}" unless
|
49
|
+
@destination_klass.public_instance_methods.include?(@destination_process_method)
|
50
|
+
end
|
51
|
+
|
52
|
+
def read_input_file
|
53
|
+
@input_file_contents ||= File.read(@input_filename)
|
54
|
+
end
|
55
|
+
|
56
|
+
def write_output_file(contents)
|
57
|
+
File.open(@output_filename, 'w') { |f| f.write(contents) }
|
58
|
+
end
|
59
|
+
|
60
|
+
def parse_transmute_options
|
61
|
+
{ stylesheets: @stylesheets }
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'transmuter/cli/thor'
|
2
|
+
require 'transmuter/cli/transmute'
|
3
|
+
|
4
|
+
module Transmuter
|
5
|
+
module CLI
|
6
|
+
class Runner < ::Thor::Group
|
7
|
+
DEFAULT_THEME = File.expand_path(File.join(ROOT_PATH, 'stylesheets', 'default.css'))
|
8
|
+
|
9
|
+
include Transmute
|
10
|
+
include Thor
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'albino'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
module Transmuter
|
5
|
+
module Format
|
6
|
+
class Html
|
7
|
+
|
8
|
+
def initialize(html, options = {})
|
9
|
+
parse_options(options)
|
10
|
+
@html = html
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_pdf
|
14
|
+
html = process
|
15
|
+
pdf = Pdf.new(html, get_options)
|
16
|
+
pdf.process
|
17
|
+
end
|
18
|
+
|
19
|
+
def process
|
20
|
+
include_inline_stylesheets(syntax_highlighter)
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
def get_options
|
25
|
+
options = @options.dup
|
26
|
+
options.delete(:redcarpet_options)
|
27
|
+
options
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse_options(options)
|
31
|
+
@options = options.dup
|
32
|
+
end
|
33
|
+
|
34
|
+
def read_stylesheet_files
|
35
|
+
case @options[:stylesheets]
|
36
|
+
when Array
|
37
|
+
@options[:stylesheets].collect do |f|
|
38
|
+
File.read(f)
|
39
|
+
end.join("\n")
|
40
|
+
when String
|
41
|
+
File.read @options[:stylesheets]
|
42
|
+
when NilClass
|
43
|
+
# Apparently no stylesheets has been requested
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def include_inline_stylesheets(html)
|
48
|
+
if @options[:stylesheets].present?
|
49
|
+
stylesheet_contents = read_stylesheet_files
|
50
|
+
|
51
|
+
doc = Nokogiri::HTML(html)
|
52
|
+
head = doc.xpath('/html/head').first
|
53
|
+
|
54
|
+
style = Nokogiri::XML::Node.new "style", doc
|
55
|
+
style['type'] = "text/css"
|
56
|
+
style.content = stylesheet_contents
|
57
|
+
|
58
|
+
unless head.present?
|
59
|
+
head = Nokogiri::XML::Node.new "head", doc
|
60
|
+
body = doc.xpath('/html/body').first
|
61
|
+
body.add_previous_sibling head
|
62
|
+
end
|
63
|
+
|
64
|
+
style.parent = head
|
65
|
+
|
66
|
+
html = doc.to_s
|
67
|
+
end
|
68
|
+
|
69
|
+
html
|
70
|
+
end
|
71
|
+
|
72
|
+
def syntax_highlighter
|
73
|
+
doc = Nokogiri::HTML(@html)
|
74
|
+
doc.search("//pre[@lang]").each do |pre|
|
75
|
+
pre.replace Albino.colorize(pre.text.rstrip, pre[:lang].downcase.to_sym)
|
76
|
+
end
|
77
|
+
doc.to_s
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'redcarpet'
|
2
|
+
|
3
|
+
module Transmuter
|
4
|
+
module Format
|
5
|
+
class Markdown
|
6
|
+
REDCARPET_OPTIONS = [:autolink, :no_intraemphasis, :fenced_code, :gh_blockcode]
|
7
|
+
|
8
|
+
def initialize(markdown, options = {})
|
9
|
+
parse_options(options)
|
10
|
+
@markdown = markdown
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_pdf
|
14
|
+
html = to_html
|
15
|
+
pdf = Pdf.new(html, get_options)
|
16
|
+
pdf.process
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_html
|
20
|
+
html = Html.new(parse_markdown, get_options)
|
21
|
+
html.process
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
def get_options
|
26
|
+
options = @options.dup
|
27
|
+
options.delete(:redcarpet_options)
|
28
|
+
options
|
29
|
+
end
|
30
|
+
|
31
|
+
def parse_options(options)
|
32
|
+
options = options.dup
|
33
|
+
@options = options.merge!(:redcarpet_options => REDCARPET_OPTIONS)
|
34
|
+
end
|
35
|
+
|
36
|
+
def create_markdown
|
37
|
+
Redcarpet.new(@markdown, *@options[:redcarpet_options])
|
38
|
+
end
|
39
|
+
|
40
|
+
def parse_markdown
|
41
|
+
create_markdown.to_html
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'pdfkit'
|
2
|
+
|
3
|
+
module Transmuter
|
4
|
+
module Format
|
5
|
+
class Pdf
|
6
|
+
|
7
|
+
def initialize(html, options = {})
|
8
|
+
parse_options(options)
|
9
|
+
@html = html
|
10
|
+
end
|
11
|
+
|
12
|
+
def process
|
13
|
+
kit = PDFKit.new(@html, :page_size => @options[:page_size])
|
14
|
+
|
15
|
+
kit.to_pdf
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
def parse_options(options)
|
20
|
+
options = options.dup
|
21
|
+
@options = options.merge!(:page_size => 'Letter')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/transmuter/version.rb
CHANGED
data/lib/transmuter.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
|
1
|
+
ROOT_PATH = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
require "transmuter/version"
|
4
|
+
require "transmuter/core_ext"
|
5
|
+
require "transmuter/format"
|
6
|
+
require "transmuter/cli"
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
ROOT_PATH = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
$:.push File.expand_path("#{ROOT_PATH}/lib")
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'rspec'
|
7
|
+
|
8
|
+
# Require the library
|
9
|
+
require 'transmuter'
|
10
|
+
|
11
|
+
include Transmuter
|
12
|
+
|
13
|
+
# Require support files
|
14
|
+
Dir[ROOT_PATH + "/spec/support/**/*.rb"].each {|f| require f}
|
15
|
+
|
16
|
+
RSpec.configure do |config|
|
17
|
+
# == Mock Framework
|
18
|
+
#
|
19
|
+
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
|
20
|
+
#
|
21
|
+
config.mock_with :mocha
|
22
|
+
# config.mock_with :flexmock
|
23
|
+
# config.mock_with :rr
|
24
|
+
# config.mock_with :rspec
|
25
|
+
end
|