ebookie 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8cbd3543662f1c4abeff2153fae912695093d4c0
4
+ data.tar.gz: be917c2395058c019bbf02fabfd3da7c6b8ddc19
5
+ SHA512:
6
+ metadata.gz: 3c5e6c82e1fc905b30cd9aff20300bc98e5d25dfa55b97e6da2ee8d5d4881a25762a3ac5edbddf8e66772568be856be94d0dbc257a3af95f1ad7ede451ee6730
7
+ data.tar.gz: 8c99693dd10cb8ee9c2eefb69f2f14b2ea5869cf2aec369d821a343ba7b81f28b572745983774e155bbaae22ff59f20c29a919f42d5e6d9000f7d4e1f4903e77
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 jordanandree
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,91 @@
1
+ # Ebookie
2
+
3
+ Generate PDF, ePub, and Mobi eBooks
4
+
5
+ [Example project](./example)
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'ebookie'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install ebookie
20
+
21
+ ## Usage
22
+
23
+ Create a new document:
24
+
25
+ ```ruby
26
+ document = Ebookie::Document.new "My eBook" # Title
27
+ ```
28
+
29
+ Configure it:
30
+
31
+ ```ruby
32
+ document.configure do |config|
33
+ config.cover = './path/to/cover.png' # Cover image
34
+ config.destination = './ebook/' # Ouput for ePub, Mobi, PDF
35
+ config.subject = "Introductions"
36
+ config.source = "http://google.com"
37
+ end
38
+ ```
39
+
40
+ Add a chapter:
41
+
42
+ ```ruby
43
+ # You can pass a string for the content
44
+ document.chapter 'Getting Started', "All about how to get started"
45
+
46
+ # You can also give it an html file to read
47
+ document.chapter 'Getting Started', Pathname.new("path/to/myfile.html")
48
+ ```
49
+
50
+ Add an image:
51
+
52
+ ```ruby
53
+ # Relative or absolute path for image to be copied
54
+ document.image './path/to/image.png'
55
+
56
+ # Chapters can reference images with html:
57
+ document.chapter "My Cool Image", "<img src='image.png' alt='image' />"
58
+ ```
59
+
60
+ Render the document:
61
+
62
+ ```ruby
63
+ # ePub
64
+ document.render_epub
65
+
66
+ # Mobi
67
+ document.render_mobi
68
+
69
+ # PDF
70
+ document.render_pdf
71
+ ```
72
+
73
+ #### Install templates for customization:
74
+
75
+ ```bash
76
+ $ ebookie install ./path/to/templatees
77
+ ```
78
+
79
+ Then configure the document:
80
+
81
+ ```ruby
82
+ document.config.template = './path/to/templates'
83
+ ```
84
+
85
+ ## Contributing
86
+
87
+ 1. Fork it ( http://github.com/mailchimp/ebookie/fork )
88
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
89
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
90
+ 4. Push to the branch (`git push origin my-new-feature`)
91
+ 5. Create new Pull Request
@@ -0,0 +1,16 @@
1
+ module Ebookie
2
+ require "ebookie/version"
3
+ require "ebookie/document"
4
+ require "ebookie/rendering"
5
+ require "logger"
6
+
7
+ def self.logger
8
+ @logger ||= Logger.new(STDOUT)
9
+ if ENV['LOG_LEVEL'] && log_level = Logger.const_get(ENV['LOG_LEVEL'])
10
+ @logger.level = log_level
11
+ else
12
+ @logger.level = Logger::WARN
13
+ end
14
+ @logger
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ require "thor"
2
+ require "pathname"
3
+
4
+ module Ebookie
5
+ class Cli < Thor
6
+ include Thor::Actions
7
+ add_runtime_options!
8
+
9
+ desc "install DIRECTORY", "install template files to DIRECTORY"
10
+ def install(directory)
11
+ exec_dir = Pathname.new(Dir.pwd)
12
+ templates_dir = Pathname.new(File.expand_path('../templates', __FILE__))
13
+
14
+ Ebookie::Cli.source_root templates_dir
15
+
16
+ directory templates_dir, exec_dir.join(directory)
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ require "ebookie/document/config"
2
+ require "ebookie/document/base"
3
+ require "ebookie/document/chapter"
4
+ require "ebookie/document/image"
5
+
6
+ module Ebookie
7
+ module Document
8
+ class << self
9
+
10
+ def new(*args)
11
+ Base.new(*args)
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,49 @@
1
+ module Ebookie
2
+ module Document
3
+ class Base
4
+
5
+ attr_reader :chapters, :images, :config
6
+
7
+ def initialize(title)
8
+ @chapters = []
9
+ @images = []
10
+
11
+ @config = Config.new
12
+ @config.title = title
13
+ end
14
+
15
+ def configure(&block)
16
+ yield @config
17
+ end
18
+
19
+ def chapter(title, content)
20
+ @chapters << Chapter.new(title, content)
21
+ end
22
+
23
+ def image(file)
24
+ @images << Image.new(file)
25
+ end
26
+
27
+ def render_pdf
28
+ Rendering::Pdf.new(self).render
29
+ end
30
+
31
+ def render_epub
32
+ Rendering::Epub.new(self).render
33
+ end
34
+
35
+ def render_mobi
36
+ Rendering::Mobi.new(self).render
37
+ end
38
+
39
+ def method_missing(meth, *args)
40
+ if @config.respond_to?(meth)
41
+ @config.send(meth, *args)
42
+ else
43
+ super
44
+ end
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,26 @@
1
+ module Ebookie
2
+ module Document
3
+ class Chapter
4
+
5
+ attr_accessor :title, :content
6
+
7
+ def initialize(title, content)
8
+ @title = title
9
+ @content = content
10
+ end
11
+
12
+ def content
13
+ if @content.is_a? Pathname
14
+ @content.read
15
+ else
16
+ @content
17
+ end
18
+ end
19
+
20
+ def slug
21
+ title.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, '')
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,43 @@
1
+ module Ebookie
2
+ module Document
3
+ class Config
4
+
5
+ attr_accessor :title
6
+ attr_accessor :creator
7
+ attr_accessor :publisher
8
+ attr_accessor :rights
9
+ attr_accessor :subject
10
+ attr_accessor :language
11
+ attr_accessor :source
12
+ attr_accessor :date
13
+ attr_accessor :destination
14
+ attr_accessor :cover
15
+ attr_accessor :template
16
+
17
+ def date
18
+ @date || Time.now.strftime("%F")
19
+ end
20
+
21
+ def language
22
+ @language || "en-US"
23
+ end
24
+
25
+ def cover=(path)
26
+ if File.exists?(path) || path.match(/http[s]?:\/\//)
27
+ if %w(.png .pdf).include? File.extname(path)
28
+ @cover = Pathname.new(path)
29
+ else
30
+ Ebookie.logger.warn "Cover file is not a valid"
31
+ end
32
+ else
33
+ Ebookie.logger.warn "Cover file does not exist"
34
+ end
35
+ end
36
+
37
+ def slug
38
+ title.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, '')
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,29 @@
1
+ module Ebookie
2
+ module Document
3
+ class Image
4
+
5
+ attr_reader :file
6
+
7
+ def initialize(filepath)
8
+ self.file = filepath
9
+ end
10
+
11
+ def file=(path)
12
+ if File.exists?(path) || path.match(/http[s]?:\/\//)
13
+ if File.extname(path) == '.png'
14
+ @file = Pathname.new(path)
15
+ else
16
+ Ebookie.logger.warn "Image file is not a valid png for '#{path}'"
17
+ end
18
+ else
19
+ Ebookie.logger.warn "Image file does not exist for '#{path}'"
20
+ end
21
+ end
22
+
23
+ def basename
24
+ File.basename(file) if file
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,10 @@
1
+ require "ebookie/rendering/base"
2
+ require "ebookie/rendering/epub"
3
+ require "ebookie/rendering/mobi"
4
+ require "ebookie/rendering/pdf"
5
+
6
+ module Ebookie
7
+ module Rendering
8
+
9
+ end
10
+ end
@@ -0,0 +1,156 @@
1
+ require "ostruct"
2
+ require "borrower"
3
+ require "erb"
4
+
5
+ module Ebookie
6
+ module Rendering
7
+ class Base
8
+ IMAGE_SRC_REGEX = /src=['|"]((\/?.+\/)*)?(.+?[\.]+.+?)['|"]/xi
9
+
10
+ attr_reader :document
11
+
12
+ def initialize(document)
13
+ @document = document
14
+
15
+ after_initialize if respond_to?(:after_initialize)
16
+ end
17
+
18
+ class << self
19
+ def inherited(subclass)
20
+ subclass.class_eval do
21
+ attr_reader :settings
22
+ end
23
+
24
+ subclass.instance_eval do
25
+ define_method :settings do
26
+ @@settings.send(format) || {}
27
+ end
28
+ end
29
+ end
30
+
31
+ def set(key, val)
32
+ @@settings ||= OpenStruct.new
33
+
34
+ format_options = @@settings.send(format) || {}
35
+ @@settings.send "#{format}=", format_options.merge({key => val})
36
+ end
37
+
38
+ def format
39
+ self.name.split("::").last.downcase
40
+ end
41
+ end
42
+
43
+ def render
44
+ throw "Output path required" unless document.destination
45
+
46
+ FileUtils.mkdir_p(tmp_dir) unless File.exists?(tmp_dir)
47
+
48
+ create_paths if settings.keys.include?(:paths) && settings[:paths]
49
+ copy_files if settings.keys.include?(:files) && settings[:files]
50
+ copy_images if document.images.any? && settings[:images_dir]
51
+
52
+ FileUtils.mkdir_p(document.destination) unless File.exists?(document.destination)
53
+
54
+ process!
55
+ return output_path
56
+ end
57
+
58
+ def template_file(path)
59
+ custom_template_dir = Pathname.new(document.template) if document.template
60
+
61
+ if document.template && File.exists?(custom_template_dir.join(format, path))
62
+ custom_template_dir.join(format, path)
63
+ else
64
+ template_dir.join(path)
65
+ end
66
+ end
67
+
68
+ def render_erb_to_file(template, filepath, locals={})
69
+ locals.merge! document: document, renderer: self
70
+
71
+ locals_struct = OpenStruct.new(locals).instance_eval { binding }
72
+ contents = ERB.new(File.read(template)).result(locals_struct)
73
+
74
+ write_contents_to_file(contents, filepath)
75
+ end
76
+
77
+ def tmp_dir
78
+ Pathname.new(File.expand_path("../../../../tmp/#{document.config.slug}/#{format}", __FILE__))
79
+ end
80
+
81
+ def template_dir
82
+ Pathname.new(File.expand_path("../../templates/#{format}", __FILE__))
83
+ end
84
+
85
+ def format
86
+ self.class.format
87
+ end
88
+
89
+ def output_path
90
+ Pathname.new(document.destination).join("#{document.config.slug}.#{format}").to_s
91
+ end
92
+
93
+ def sanitize_html(html)
94
+ {
95
+ /&rsquo;|&lsquo;/ => "'",
96
+ /&rdquo;|&ldquo;|“|”/ => "\"",
97
+ "’" => "'",
98
+ "&#58;" => ":",
99
+ "⌘" => "&#8984;"
100
+ }.each do |k,v|
101
+ html.gsub! k, v
102
+ end
103
+
104
+ html
105
+ end
106
+
107
+ def clean_images(html, new_path)
108
+ html.each_line do |line|
109
+ old_line = line.dup
110
+ matches = line.match(IMAGE_SRC_REGEX).to_a
111
+ next unless matches.any?
112
+
113
+ # remove folder
114
+ line.gsub! matches[2], "" if matches[2]
115
+
116
+ # set our folder
117
+ line.gsub! matches[3], new_path.join(matches[3]).to_s
118
+
119
+ html.gsub! old_line, line
120
+ end
121
+ end
122
+
123
+ private
124
+
125
+ def create_paths
126
+ settings[:paths].each do |path|
127
+ FileUtils.mkdir_p tmp_dir.join(path)
128
+ end
129
+ end
130
+
131
+ def copy_images
132
+ document.images.each do |image|
133
+ borrow image.file.to_s, to: tmp_dir.join(settings[:images_dir], image.basename)
134
+ end
135
+ end
136
+
137
+ def copy_files
138
+ settings[:files].each do |file|
139
+ if File.extname(file) == '.erb' && ext = File.extname(file)
140
+ render_erb_to_file template_file(file), tmp_dir.join(file.gsub(ext, ''))
141
+ else
142
+ FileUtils.cp template_file(file), tmp_dir.join(file)
143
+ end
144
+ end
145
+ end
146
+
147
+ def write_contents_to_file(contents, filepath, mode="w+")
148
+ File.open(filepath, mode) do |handle|
149
+ handle.write(contents)
150
+ handle.close
151
+ end
152
+ end
153
+
154
+ end
155
+ end
156
+ end