karmi-markout 0.0.0

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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Karel Minarik
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,23 @@
1
+ = Markout
2
+
3
+ Markout makes it easy to export sexy formatted documents such as specifications from your plain text, Markdown-formatted files.
4
+
5
+ Designed for easy extending, theming and adding output formats.
6
+
7
+ More information soon.
8
+
9
+
10
+ == Usage
11
+
12
+ On the command line:
13
+
14
+ $ sudo gem install karmi-markout --source=http://gems.github.com
15
+
16
+
17
+ == Todo
18
+
19
+ [!] Visualize document history based on Git information
20
+
21
+ == Copyright
22
+
23
+ Copyright (c) 2009 Karel Minarik. Released under MIT license.
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :minor: 0
3
+ :patch: 0
4
+ :major: 0
data/bin/markout ADDED
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'optparse'
5
+
6
+ require File.join( File.join(File.dirname(__FILE__), '..', 'lib', 'markout') )
7
+
8
+ opts = {}
9
+
10
+ def die(msg)
11
+ STDERR.puts("\033[41;1mError!\033[0m\n" + msg)
12
+ exit 1
13
+ end
14
+
15
+ OptionParser.new do |o|
16
+ o.banner = "USAGE: #{$0} [options] [path/to/file]"
17
+
18
+ o.on("-f",
19
+ "--format [FORMAT]",
20
+ "Output format (html, pdf)") do |f|
21
+ opts[:format] = f
22
+ end
23
+
24
+ o.on("-t",
25
+ "--template [TEMPLATE]",
26
+ "Output template") do |t|
27
+ opts[:template] = t
28
+ end
29
+
30
+ o.on("-o",
31
+ "--output [path/to/directory]",
32
+ "Output directory (default: same as source file)") do |d|
33
+ die('Output directory does not exist!') unless File.exist?(d)
34
+ opts[:output] = d
35
+ end
36
+
37
+ o.on("-h", "--help", "Show help") do |h|
38
+ puts o
39
+ exit
40
+ end
41
+ end.parse!
42
+
43
+ die("You need to provide path to a Markdown formatted text file!
44
+ USAGE: #{$0} [options] [path/to/file]
45
+ Type #{$0} -h for help") if ARGV.empty?
46
+
47
+ begin
48
+ time = Time.now
49
+ o = Markout::Output.new( ARGV.first, opts )
50
+ puts "\033[0mConverting \033[1m#{ARGV[0]}\033[30;0m to \033[1m#{o.output_path}\033[0m"
51
+ o.export(true)
52
+ puts "\033[42mMarkout finished\033[0m in #{sprintf('%.2f', Time.now-time)} seconds\n"
53
+ rescue Exception => e
54
+ puts "\033[41;1mMarkout Failed!\033[0m\n"
55
+ raise e
56
+ end
data/lib/markout.rb ADDED
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH << File.join( File.dirname(__FILE__), 'markout' )
2
+
3
+ %w{output document formatter}.each { |lib| require lib }
4
+
5
+ module Markout
6
+
7
+ class FileNotFound < Exception; end
8
+
9
+ end
@@ -0,0 +1,20 @@
1
+ require 'pathname'
2
+ require 'mime/types'
3
+
4
+ module Markout
5
+
6
+ class Document
7
+
8
+ attr_reader :path, :base_path, :content
9
+
10
+ def initialize(path)
11
+ @path = File.expand_path(path)
12
+ @base_path = Pathname.new( File.dirname(@path) )
13
+ raise FileNotFound, "File #{@path} not found" unless File.exist?(@path)
14
+ # TODO: Raise error for non-text file
15
+ @content = File.read(path)
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,25 @@
1
+ require 'pathname'
2
+
3
+ module Markout
4
+
5
+ class Formatter
6
+
7
+ def initialize(document, options={})
8
+ @document = document
9
+ @options = options
10
+ @format = self.class.to_s.gsub(/^.*::/, '').downcase
11
+ end
12
+
13
+ def export
14
+ raise NoMethodError, "Return String in `export()` method of your formatter (#{@format})"
15
+ end
16
+
17
+ def filename
18
+ basename = File.basename(@document.path).split('.')
19
+ ext = basename.pop
20
+ "#{basename.join('.')}.#{@format}"
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,59 @@
1
+ require 'rdiscount'
2
+ require 'erb'
3
+ require 'base64'
4
+ require 'mime/types'
5
+
6
+ module Markout
7
+
8
+ class Html < Formatter
9
+
10
+ def export
11
+ suck_in_images!
12
+ ERB.new( File.read(template_path.join('content.rhtml')) ).result(binding)
13
+ end
14
+
15
+ private
16
+
17
+ def template_name
18
+ @options[:template] || 'default'
19
+ end
20
+
21
+ def template_path
22
+ Pathname.new( File.join( File.dirname(__FILE__), 'templates', template_name ) )
23
+ end
24
+
25
+ def content
26
+ @content ||= RDiscount.new( @document.content ).to_html
27
+ end
28
+
29
+ def screen_style
30
+ File.read template_path.join('screen.css')
31
+ end
32
+
33
+ def print_style
34
+ File.read template_path.join('print.css')
35
+ end
36
+
37
+ def title
38
+ h1 = content.match(/<h1\s*.*>(.+)<\/h1>/)[1] rescue nil
39
+ h1 || ''
40
+ end
41
+
42
+ # TODO : Make 'alt' attribute optional
43
+ # TODO : Cleanup?
44
+ def suck_in_images!
45
+ content.to_s.gsub!(/<img src="([^"]+)".*alt="([^"]+)".*\s*\/?>/) do |match|
46
+ begin
47
+ file = File.read( @document.base_path.join($1) )
48
+ mime = MIME::Types.of(File.basename( @document.base_path.join($1) ))
49
+ "<img src=\"data:#{mime};base64,#{Base64.encode64(file)}\" alt=\"#{$2}\" />"
50
+ rescue
51
+ # TODO : Better error handling
52
+ puts "Error: Cannot load image #{$1}"
53
+ end
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -0,0 +1,27 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
+
4
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
5
+ <head>
6
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
7
+
8
+ <title><%= title %></title>
9
+
10
+ <style type="text/css" media="screen">
11
+ <%= screen_style %>
12
+ </style>
13
+
14
+ <style type="text/css" media="print">
15
+ <%= print_style %>
16
+ </style>
17
+
18
+ </head>
19
+
20
+ <body>
21
+
22
+ <div id="content">
23
+ <%= content %>
24
+ </div>
25
+
26
+ </body>
27
+ </html>
@@ -0,0 +1,34 @@
1
+ body
2
+ { color: #000;
3
+ background: #fff;
4
+ font-size: 85%;
5
+ font-family: Helvetica, sans-serif;
6
+ margin: 0.5cm; }
7
+
8
+ a
9
+ { color: #000;
10
+ text-decoration: underline; }
11
+
12
+ a img
13
+ { border: none; }
14
+
15
+ h2 {
16
+ font-size: 1.5em;
17
+ letter-spacing: -0.05em;
18
+ padding: 1.15em 0 0 0;
19
+ margin: 1.25em 0 0.25em 0;
20
+ border-top: 1mm solid #999;
21
+ }
22
+
23
+ h3 {
24
+ font-size: 1.1em;
25
+ font-weight:bold;
26
+ margin: 1.8em 0 0.25em 0;
27
+ letter-spacing: -0.05em;
28
+ }
29
+
30
+ pre {
31
+ border: 1pt solid #ccc;
32
+ padding: 0.5em;
33
+ white-space: pre-wrap; white-space: -moz-pre-wrap !important;
34
+ }
@@ -0,0 +1,102 @@
1
+ /*
2
+ = COLORS =
3
+
4
+ Steel blue #d8dde3
5
+ Black blue #172533
6
+ Dark grey #525c66
7
+ Ash grey #dfe2e5
8
+ Live blue #1177DD
9
+ Pale green face #b9cbdd
10
+
11
+ */
12
+
13
+ body {
14
+ color: #222;
15
+ background: #fafafa;
16
+ font-family: Helvetica, sans-serif;
17
+ font-size: 85%;
18
+ line-height: 155%;
19
+ margin: 0;
20
+ padding: 0 5em;
21
+ }
22
+
23
+ .cleaner
24
+ { clear: both; height: 0; line-height: 0; width: 0; border: 0; font-size: 1px; }
25
+
26
+ #content {
27
+ color: #172533;
28
+ font-size: 1.1em;
29
+ max-width: 55em;
30
+ padding-bottom: 4em;
31
+ margin: 7px auto;
32
+ margin-bottom: 4em;
33
+ border-top: 4px solid #172533;
34
+ border-bottom: 4px solid #b9cbdd;
35
+ }
36
+
37
+ h1, h2, h3, h4, h5, h6 {
38
+ color: #222;
39
+ }
40
+ h1, h2 {
41
+ color: #172533;
42
+ font-weight: normal;
43
+ }
44
+ h1 a, h2 a { color: #172533; }
45
+
46
+ h1 {
47
+ font-size: 2.25em;
48
+ letter-spacing: -1px;
49
+ margin-bottom: 0.5em;
50
+ }
51
+
52
+ h2 {
53
+ font-size: 1.5em;
54
+ letter-spacing: -1px;
55
+ padding: 1.15em 0 0 0;
56
+ margin: 1.25em 0 0.25em 0;
57
+ border-top: 4px solid #d8dde3;
58
+ }
59
+
60
+ h3 {
61
+ color: #172533;
62
+ font-size: 1.1em;
63
+ font-weight:bold;
64
+ margin: 1.8em 0 0.25em 0;
65
+ letter-spacing:-1px;
66
+ }
67
+
68
+ h3 a {
69
+ text-decoration:underline;
70
+ }
71
+
72
+ p {
73
+ margin: 0 0 0.8em 0;
74
+ }
75
+
76
+ a {
77
+ color: #172533;
78
+ text-decoration:underline;
79
+ }
80
+ a:hover {
81
+ color: #1177DD;
82
+ text-decoration:underline;
83
+ }
84
+ a img {
85
+ border:none;
86
+ }
87
+
88
+ code, pre, textarea, tt {
89
+ font-family: "Monaco", "lucida console", "bitstream vera sans mono", monospace;
90
+ font-size: 100%;
91
+ }
92
+ pre {
93
+ color: #172533;
94
+ background: #f5f9fc;
95
+ font-size: 100%;
96
+ line-height: 155%;
97
+ border: 4px solid #dfe2e5;
98
+ padding: 0.5em;
99
+ white-space: pre-wrap; white-space: -moz-pre-wrap !important;
100
+ }
101
+
102
+ hr { display: none; }
@@ -0,0 +1,27 @@
1
+ require 'tempfile'
2
+ require File.join(File.dirname(__FILE__), '..', 'html', 'html.rb')
3
+
4
+ # Depends on HTMLDOC utility (http://www.easysw.com/htmldoc/)
5
+ module Markout
6
+
7
+ class Pdf < Formatter
8
+
9
+ def export
10
+ `cat "#{tempfile.path}" | /opt/local/bin/htmldoc -t pdf \
11
+ --bodyfont "Helvetica" --headfootfont "Helvetica" \
12
+ --no-compression --color --embedfonts \
13
+ --header "" --footer .1. --links --no-title \
14
+ --toctitle "" --tocheader "..." --tocfooter "..." \
15
+ -`
16
+ end
17
+
18
+ private
19
+
20
+ def tempfile
21
+ tempfile = Tempfile.new(File.basename(@document.path) + '.html')
22
+ tempfile << Markout::Html.new(@document).export
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,49 @@
1
+ module Markout
2
+
3
+ class Output
4
+
5
+ attr_reader :document, :format, :formatter
6
+
7
+ def initialize(path, options = {})
8
+ @path = path
9
+ @document = Document.new(@path)
10
+ @options = options
11
+ @format = @options[:format] || 'html'
12
+ @formatter = pick_formatter.new( @document, :template => @options[:template] || 'default' )
13
+ @output = ''
14
+ end
15
+
16
+ def export(to_file=false)
17
+ @output = @formatter.export
18
+ write_file if to_file
19
+ return @output
20
+ end
21
+
22
+ def output_path
23
+ if @options[:output]
24
+ File.join( File.expand_path( @options[:output] ), @formatter.filename )
25
+ else
26
+ @document.base_path.join(@formatter.filename)
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def write_file
33
+ File.open(output_path, 'w') { |f| f.write @output }
34
+ end
35
+
36
+ def pick_formatter
37
+ unless Markout::const_defined?(@format.to_s.capitalize)
38
+ begin
39
+ require File.join( File.dirname(__FILE__), 'formatters', @format, @format )
40
+ rescue Exception => e
41
+ raise LoadError, "Formatter '#{@format}' not found! (#{e.message})"
42
+ end
43
+ end
44
+ Markout::const_get(@format.to_s.capitalize)
45
+ end
46
+
47
+ end
48
+
49
+ end