epubforge 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/config/actions/init.rb +2 -3
- data/config/actions/kindle.rb +3 -3
- data/config/actions/mobify.rb +69 -0
- data/config/htmlizers.rb +8 -0
- data/lib/action/run_description.rb +56 -6
- data/lib/action/runner.rb +5 -9
- data/lib/action/thor_action.rb +32 -4
- data/lib/core_extensions/kernel.rb +0 -20
- data/lib/custom_helpers.rb +28 -27
- data/lib/epub/assets/image.rb +2 -3
- data/lib/epub/assets/page.rb +33 -6
- data/lib/epub/assets/xhtml.rb +8 -0
- data/lib/epub/builder.rb +47 -18
- data/lib/epubforge.rb +2 -1
- data/lib/utils/class_loader.rb +1 -2
- data/lib/utils/file_orderer.rb +9 -9
- data/lib/utils/file_path.rb +1 -1
- data/lib/utils/html_translator.rb +2 -1
- data/lib/utils/htmlizer.rb +1 -1
- data/templates/default/book/cover.xhtml.template +13 -0
- data/templates/default/notes/images/cover.png +0 -0
- data/templates/default/settings/config.rb.form +1 -1
- data/test/test_epubforge.rb +10 -9
- metadata +7 -3
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.6
|
data/config/actions/init.rb
CHANGED
@@ -15,7 +15,7 @@ module EpubForge
|
|
15
15
|
|
16
16
|
@template_dir = EpubForge.root.join( "templates", @template_to_use )
|
17
17
|
src_entries = @template_dir.glob( "**", "*" ).map{ |entry|
|
18
|
-
entry.
|
18
|
+
entry.relative_path_from( @template_dir )
|
19
19
|
}
|
20
20
|
|
21
21
|
self.source_paths.push( @template_dir )
|
@@ -74,7 +74,7 @@ module EpubForge
|
|
74
74
|
configure_git( opts[:git] || {} )
|
75
75
|
end
|
76
76
|
else
|
77
|
-
|
77
|
+
warn("The program 'git' must be installed and locatable if you want epubforge to back up your project.")
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
@@ -121,7 +121,6 @@ module EpubForge
|
|
121
121
|
end
|
122
122
|
|
123
123
|
self.destination_root_filepath = root.fwf_filepath
|
124
|
-
debugger if self.destination_root_filepath != root.fwf_filepath.expand
|
125
124
|
|
126
125
|
if self.destination_root_filepath.exist? &&
|
127
126
|
(!(self.destination_root_filepath.empty?) || !(self.destination_root_filepath.directory?))
|
data/config/actions/kindle.rb
CHANGED
@@ -4,6 +4,7 @@ module EpubForge
|
|
4
4
|
description "Create a .mobi book and try to push it to your Kindle (conversion requires Calibre)"
|
5
5
|
keywords :kindle, :push, :b2k
|
6
6
|
usage "#{$PROGRAM_NAME} b2k <project_directory>"
|
7
|
+
# requires_executable "ebook-convert"
|
7
8
|
|
8
9
|
# TODO: Hard-coded. Need a global settings file?
|
9
10
|
KINDLE_DEVICE_DIR = "/".fwf_filepath.join( "Volumes", "Kindle" )
|
@@ -15,7 +16,6 @@ module EpubForge
|
|
15
16
|
@src_epub = @project.filename_for_epub_book.fwf_filepath
|
16
17
|
@dst_mobi = @project.filename_for_mobi_book.fwf_filepath
|
17
18
|
|
18
|
-
mobify
|
19
19
|
end
|
20
20
|
|
21
21
|
protected
|
@@ -31,8 +31,8 @@ module EpubForge
|
|
31
31
|
say_all_is_well "Formatted for Kindle (.mobi file). File at #{@dst_mobi}."
|
32
32
|
push_to_device @dst_mobi
|
33
33
|
else
|
34
|
-
|
35
|
-
|
34
|
+
warn( "Something went wrong during the conversion process." )
|
35
|
+
warn( "#{@dst_mobi} exists, but may not be complete or correct." ) if @dst_mobi.exist?
|
36
36
|
false
|
37
37
|
end
|
38
38
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module EpubForge
|
2
|
+
module Action
|
3
|
+
class Mobify < ThorAction
|
4
|
+
description "Create a .mobi book and try to push it to your Kindle (conversion requires Calibre)"
|
5
|
+
keywords :mobify
|
6
|
+
usage "#{$PROGRAM_NAME} mobify <project_directory(optional)>"
|
7
|
+
# requires_executable "ebook-convert", "ebook-convert is included as part of the Calibre ebook management software."
|
8
|
+
|
9
|
+
desc( "do:kindle", "Turn your .epub file into a .mobi file. Check to see if your Kindle is connected, then pushes it." )
|
10
|
+
def do( project, *args )
|
11
|
+
@project = project
|
12
|
+
@src_epub = @project.filename_for_epub_book.fwf_filepath
|
13
|
+
@dst_mobi = @project.filename_for_mobi_book.fwf_filepath
|
14
|
+
|
15
|
+
@args = args
|
16
|
+
|
17
|
+
mobify
|
18
|
+
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
def mobify
|
24
|
+
return false unless fulfill_requirements
|
25
|
+
|
26
|
+
say "converting from #{@src_epub} to #{@dst_mobi}"
|
27
|
+
cmd = "ebook-convert #{@src_epub} #{@dst_mobi}"
|
28
|
+
say "executing: #{cmd}"
|
29
|
+
`#{cmd}`
|
30
|
+
|
31
|
+
if $?.success? && @dst_mobi.exist?
|
32
|
+
say_all_is_well "Formatted for Kindle (.mobi file). File at #{@dst_mobi}."
|
33
|
+
push_to_device @dst_mobi
|
34
|
+
else
|
35
|
+
warn( "Something went wrong during the conversion process." )
|
36
|
+
warn( "#{@dst_mobi} exists, but may not be complete or correct." ) if @dst_mobi.exist?
|
37
|
+
false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def push_to_device mobi_file
|
42
|
+
if KINDLE_DEVICE_DIR.directory? && KINDLE_PUSH_DIR.directory?
|
43
|
+
FileUtils.copy( mobi_file, KINDLE_PUSH_DIR )
|
44
|
+
say_all_is_well "File pushed to Kindle."
|
45
|
+
true
|
46
|
+
else
|
47
|
+
say_error "NOT installed on Kindle. It may not be plugged in."
|
48
|
+
false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def fulfill_requirements
|
53
|
+
unless ebook_convert_installed?
|
54
|
+
say_error( "ebook-convert is not installed. Quitting." )
|
55
|
+
return false
|
56
|
+
end
|
57
|
+
|
58
|
+
BookToEpub.new.do( @project )
|
59
|
+
|
60
|
+
unless @src_epub.exist?
|
61
|
+
say_error( "Cannot find source .epub #{src_epub}" )
|
62
|
+
return false
|
63
|
+
end
|
64
|
+
|
65
|
+
true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/config/htmlizers.rb
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
EpubForge::Utils::Htmlizer.define do |html|
|
2
|
+
html.format :xhtml
|
3
|
+
html.group :default
|
4
|
+
html.executable "false"
|
5
|
+
html.cmd "cat {{f}}"
|
6
|
+
end
|
7
|
+
|
8
|
+
|
1
9
|
EpubForge::Utils::Htmlizer.define do |html|
|
2
10
|
html.format :markdown
|
3
11
|
html.group :default # the default is :user, so user-defined ones don't have to set it
|
@@ -1,11 +1,13 @@
|
|
1
1
|
module EpubForge
|
2
2
|
module Action
|
3
3
|
class RunDescription
|
4
|
-
attr_accessor :args
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
attr_accessor :args,
|
5
|
+
:project,
|
6
|
+
:keyword,
|
7
|
+
:klass,
|
8
|
+
:errors,
|
9
|
+
:state,
|
10
|
+
:execution_returned
|
9
11
|
|
10
12
|
def initialize
|
11
13
|
@args = nil
|
@@ -13,10 +15,58 @@ module EpubForge
|
|
13
15
|
@keyword = nil
|
14
16
|
@klass = nil
|
15
17
|
@errors = []
|
18
|
+
@state = :initialized
|
16
19
|
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
if self.runnable?
|
23
|
+
handle_errors do
|
24
|
+
@execution_returned = self.klass.new.do( self.project, *(self.args) )
|
25
|
+
end
|
26
|
+
else
|
27
|
+
puts "Error(s) trying to complete the requested action:"
|
28
|
+
puts self.errors.join("\n")
|
29
|
+
end
|
30
|
+
|
31
|
+
self.finish
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def handle_errors &block
|
36
|
+
yield
|
37
|
+
rescue Exception => e
|
38
|
+
@errors << "#{e.class}: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
17
42
|
|
18
43
|
def runnable?
|
19
|
-
|
44
|
+
! errors?
|
45
|
+
end
|
46
|
+
|
47
|
+
def errors?
|
48
|
+
!@errors.epf_blank?
|
49
|
+
end
|
50
|
+
|
51
|
+
def success?
|
52
|
+
finished? && ! errors?
|
53
|
+
end
|
54
|
+
|
55
|
+
def finished?
|
56
|
+
@state == :finished
|
57
|
+
end
|
58
|
+
|
59
|
+
def finish
|
60
|
+
@state = :finished
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_s
|
64
|
+
str = "RunDescription:\n"
|
65
|
+
[ :args, :project, :keyword, :klass, :errors, :state ].each do |data|
|
66
|
+
str << "#{data} : #{self.send(data)}\n"
|
67
|
+
end
|
68
|
+
|
69
|
+
str
|
20
70
|
end
|
21
71
|
end
|
22
72
|
end
|
data/lib/action/runner.rb
CHANGED
@@ -17,12 +17,9 @@ module EpubForge
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def run
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
puts "Error(s) trying to complete the requested action:"
|
24
|
-
puts @run_description.errors.join("\n")
|
25
|
-
end
|
20
|
+
@run_description.run
|
21
|
+
puts @run_description
|
22
|
+
@run_description
|
26
23
|
end
|
27
24
|
|
28
25
|
# order: project_dir(optional), keyword, args
|
@@ -36,9 +33,6 @@ module EpubForge
|
|
36
33
|
# print help message if no keywords given
|
37
34
|
parse_args
|
38
35
|
|
39
|
-
# finish setting up run_description
|
40
|
-
@run_description.args = @args
|
41
|
-
|
42
36
|
run
|
43
37
|
end
|
44
38
|
|
@@ -86,6 +80,8 @@ module EpubForge
|
|
86
80
|
|
87
81
|
if !existing_project && @run_description.klass.project_required?
|
88
82
|
@run_description.errors << "Could not find a project directory, but the action #{@run_description.klass} requires one. Current directory is not an epubforge project."
|
83
|
+
else
|
84
|
+
@run_description.args = @args
|
89
85
|
end
|
90
86
|
end
|
91
87
|
|
data/lib/action/thor_action.rb
CHANGED
@@ -40,6 +40,9 @@ module EpubForge
|
|
40
40
|
include Thor::Actions
|
41
41
|
extend SharedActionInterface
|
42
42
|
|
43
|
+
method_option :verbose, :type => :boolean, :default => false, :aliases => "-v"
|
44
|
+
method_option :debug, :type => :boolean, :default => false, :aliases => "--dbg"
|
45
|
+
|
43
46
|
|
44
47
|
CLEAR = Thor::Shell::Color::CLEAR
|
45
48
|
RED = Thor::Shell::Color::RED
|
@@ -52,6 +55,14 @@ module EpubForge
|
|
52
55
|
|
53
56
|
|
54
57
|
protected
|
58
|
+
def say_when_verbose( *args )
|
59
|
+
say( *args ) if @verbose
|
60
|
+
end
|
61
|
+
|
62
|
+
def say_when_debugging( *args )
|
63
|
+
say( *args ) if @debug
|
64
|
+
end
|
65
|
+
|
55
66
|
def say_error( statement )
|
56
67
|
say( "ERROR : #{statement}", RED + ON_BLUE )
|
57
68
|
end
|
@@ -64,10 +75,6 @@ module EpubForge
|
|
64
75
|
say( statement, GREEN )
|
65
76
|
end
|
66
77
|
|
67
|
-
def say_in_warning( statement )
|
68
|
-
warn( statement, RED )
|
69
|
-
end
|
70
|
-
|
71
78
|
def say_subtly( statement )
|
72
79
|
say( statement, MAGENTA )
|
73
80
|
end
|
@@ -133,6 +140,27 @@ module EpubForge
|
|
133
140
|
@executables[name]
|
134
141
|
end
|
135
142
|
|
143
|
+
def requirements
|
144
|
+
@requirements ||= []
|
145
|
+
end
|
146
|
+
|
147
|
+
def add_requirement( *args, &block )
|
148
|
+
@requirements ||= []
|
149
|
+
@requirements.push( [args, block] )
|
150
|
+
end
|
151
|
+
|
152
|
+
def requires_executable( ex, fail_msg )
|
153
|
+
add_requirement( ex, fail_msg ) do
|
154
|
+
executable_installed?( ex, fail_msg )
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def must_target_a_project( project_dir )
|
159
|
+
add_requirement( project_dir ) do
|
160
|
+
Project.is_project_dir?( project_dir )
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
136
164
|
def git_installed?
|
137
165
|
executable_installed?('git')
|
138
166
|
end
|
@@ -1,24 +1,4 @@
|
|
1
1
|
module Kernel
|
2
|
-
# def puts_nowhere( &block )
|
3
|
-
# collect_stdout( "/dev/null", &block )
|
4
|
-
# end
|
5
|
-
#
|
6
|
-
# def collect_stdout( dest, &block )
|
7
|
-
# @puts_nowhere_keep_old_stdout ||= []
|
8
|
-
# @puts_nowhere_keep_old_stdout << $stdout
|
9
|
-
# if dest.is_a?(String)
|
10
|
-
# $stdout = File.open( dest, "w" )
|
11
|
-
# else
|
12
|
-
# $stdout = dest
|
13
|
-
# end
|
14
|
-
#
|
15
|
-
# $stdout.sync = true
|
16
|
-
#
|
17
|
-
# yield
|
18
|
-
#
|
19
|
-
# $stdout = @puts_nowhere_keep_old_stdout.pop
|
20
|
-
# end
|
21
|
-
|
22
2
|
# yields an alternate reality block where instance methods
|
23
3
|
# are different from what they were. At the end of the block
|
24
4
|
# it resets the initial values of the instance variables.
|
data/lib/custom_helpers.rb
CHANGED
@@ -27,33 +27,34 @@ module EpubForge
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def collect_stdout( dest = StringIO.new, &block )
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
30
|
+
raise ArgumentError.new("No block given.") unless block_given?
|
31
|
+
|
32
|
+
prior_stdout = $stdout
|
33
|
+
# @epf_prior_stdout_stack ||= []
|
34
|
+
# @epf_prior_stdout_stack << $stdout
|
35
|
+
|
36
|
+
$stdout = begin
|
37
|
+
if dest.is_a?( String ) || dest.is_a?( Pathname )
|
38
|
+
File.open( dest, "a" )
|
39
|
+
elsif dest.is_a?( IO ) || dest.is_a?( StringIO )
|
40
|
+
dest
|
41
|
+
else
|
42
|
+
raise ArgumentError.new("collect_stdout cannot take a <#{dest.class.name}> as an argument.")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
$stdout.sync = true
|
47
|
+
yield
|
48
|
+
|
49
|
+
$stdout = prior_stdout
|
50
|
+
|
51
|
+
dest.is_a?( StringIO ) ? dest.string : nil
|
52
|
+
end
|
53
|
+
|
54
|
+
# def collect_stdout( *args, &block )
|
55
|
+
# yield
|
56
|
+
# ""
|
57
|
+
# end
|
57
58
|
end
|
58
59
|
end
|
59
60
|
|
data/lib/epub/assets/image.rb
CHANGED
@@ -5,12 +5,11 @@ module EpubForge
|
|
5
5
|
attr_reader :ext, :filename, :name
|
6
6
|
def initialize( filename, options = {} )
|
7
7
|
@filename = filename.fwf_filepath
|
8
|
-
@name
|
9
|
-
@ext = @filename.extname.gsub( /^\./, "" )
|
8
|
+
@name, @ext = @filename.basename_and_ext
|
10
9
|
end
|
11
10
|
|
12
11
|
def link
|
13
|
-
IMAGES_DIR.join( @name
|
12
|
+
IMAGES_DIR.join( "#{@name}.#{@ext}" )
|
14
13
|
end
|
15
14
|
end
|
16
15
|
end
|
data/lib/epub/assets/page.rb
CHANGED
@@ -2,30 +2,57 @@ module EpubForge
|
|
2
2
|
module Epub
|
3
3
|
module Assets
|
4
4
|
class Page < Asset
|
5
|
-
attr_reader :html, :original_file, :title, :project, :media_type, :dest_extension
|
6
|
-
|
5
|
+
attr_reader :html, :original_file, :title, :project, :media_type, :dest_extension, :dest_filename
|
6
|
+
attr_reader :section_id
|
7
7
|
|
8
8
|
def initialize file, metadata, project
|
9
9
|
raise "NIL" if project.nil?
|
10
|
+
|
11
|
+
@original_file = file.fwf_filepath
|
10
12
|
|
11
13
|
@metadata = metadata
|
12
14
|
@project = project
|
13
|
-
@original_file = file
|
14
15
|
@dest_extension = "xhtml"
|
16
|
+
@section_id = @original_file.basename_no_ext
|
17
|
+
@dest_filename = "#{@section_id}.#{@dest_extension}"
|
15
18
|
|
16
|
-
|
17
|
-
|
19
|
+
get_html
|
20
|
+
get_title
|
21
|
+
|
18
22
|
@content = ""
|
23
|
+
@cover = false
|
19
24
|
puts "Initialized #{file} with title [#{@title}]"
|
20
25
|
end
|
21
26
|
|
27
|
+
def get_html
|
28
|
+
@html = Utils::Htmlizer.instance.translate( @original_file )
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_title
|
32
|
+
@title = @original_file.basename.to_s.split(".")[0..-2].map(&:capitalize).join(" : ")
|
33
|
+
end
|
34
|
+
|
22
35
|
def link
|
23
|
-
TEXT_DIR.join( "
|
36
|
+
TEXT_DIR.join( "#{@section_id}.#{@dest_extension}" )
|
24
37
|
end
|
25
38
|
|
26
39
|
def media_type
|
27
40
|
MEDIA_TYPES[@dest_extension]
|
28
41
|
end
|
42
|
+
|
43
|
+
def cover( val = nil )
|
44
|
+
unless val.nil?
|
45
|
+
@cover = val
|
46
|
+
end
|
47
|
+
@cover
|
48
|
+
end
|
49
|
+
|
50
|
+
def cover_asset( val = nil )
|
51
|
+
unless val.nil?
|
52
|
+
@cover = val
|
53
|
+
end
|
54
|
+
@cover
|
55
|
+
end
|
29
56
|
end
|
30
57
|
end
|
31
58
|
end
|
data/lib/epub/builder.rb
CHANGED
@@ -3,7 +3,7 @@ XmlBuilder = Builder
|
|
3
3
|
|
4
4
|
module EpubForge
|
5
5
|
module Epub
|
6
|
-
PAGE_FILE_EXTENSIONS = %w(html markdown textile)
|
6
|
+
PAGE_FILE_EXTENSIONS = %w(html markdown textile xhtml)
|
7
7
|
IMAGE_FILE_EXTENSIONS = %w(jpg png gif)
|
8
8
|
|
9
9
|
MEDIA_TYPES = { "gif" => "image/gif", "jpg" => "image/jpeg", "png" => "image/png",
|
@@ -26,12 +26,12 @@ module EpubForge
|
|
26
26
|
@book_dir = @project.target_dir.join( @book_dir_short ).fwf_filepath
|
27
27
|
@config = @project.config
|
28
28
|
|
29
|
-
@config
|
29
|
+
@config.page_orderer = Utils::FileOrderer.new( opts[:page_order] || @config.pages[@book_dir_short] )
|
30
30
|
|
31
|
-
@metadata = @config
|
31
|
+
@metadata = @config.metadata || {}
|
32
32
|
|
33
33
|
page_files = @book_dir.glob( ext: PAGE_FILE_EXTENSIONS )
|
34
|
-
@section_files = @config
|
34
|
+
@section_files = @config.page_orderer.reorder( page_files )
|
35
35
|
|
36
36
|
@sections = @section_files.map do |section|
|
37
37
|
case section.to_s.split(".").last
|
@@ -41,21 +41,42 @@ module EpubForge
|
|
41
41
|
Assets::HTML.new( section, @metadata, self )
|
42
42
|
when "textile"
|
43
43
|
Assets::Textile.new( section, @metadata, self )
|
44
|
+
when "xhtml"
|
45
|
+
Assets::XHTML.new( section, @metadata, self ) # These files are inserted into the book unaltered
|
44
46
|
else
|
45
47
|
raise "UNKNOWN EXTENSION TYPE"
|
46
48
|
end
|
47
49
|
end
|
48
50
|
|
49
|
-
@sections.each_with_index{ |sec, i| sec.section_number = i }
|
51
|
+
# @sections.each_with_index{ |sec, i| sec.section_number = i }
|
50
52
|
|
51
53
|
images = @book_dir.glob( "images", ext: IMAGE_FILE_EXTENSIONS )
|
52
54
|
@images = images.map{ |img| Assets::Image.new(img) }
|
53
|
-
|
55
|
+
|
54
56
|
@stylesheets = @book_dir.glob( "stylesheets", "*.css" ).map do |sheet|
|
55
57
|
Assets::Stylesheet.new( sheet )
|
56
58
|
end
|
57
59
|
|
58
|
-
|
60
|
+
install_cover
|
61
|
+
|
62
|
+
@scratch_dir = FunWith::Files::FilePath.tmpdir.join( "ebookdir" )
|
63
|
+
end
|
64
|
+
|
65
|
+
def install_cover
|
66
|
+
# Existing cover is moved to the very front
|
67
|
+
if cover_section = @sections.detect{|sec| sec.original_file.basename_no_ext.match( /^cover$/ ) }
|
68
|
+
cover_section.cover( true )
|
69
|
+
elsif cover_image = @images.find{ |img| img.name == "cover" }
|
70
|
+
# actually install cover
|
71
|
+
contents = "<div id='cover'><img class='cover' src='#{cover_image.link.relative_path_from(TEXT_DIR)}' alt='#{@metadata.name}, by #{@metadata.author}'/></div>"
|
72
|
+
cover_file = @project.book_dir.join( "cover.xhtml" )
|
73
|
+
cover_file.write( wrap_page( contents ) )
|
74
|
+
cover_section = Assets::Page.new( cover_file, @metadata, @project )
|
75
|
+
@sections.unshift( cover_section )
|
76
|
+
puts "cover page generated"
|
77
|
+
else
|
78
|
+
return false
|
79
|
+
end
|
59
80
|
end
|
60
81
|
|
61
82
|
def toc
|
@@ -76,6 +97,7 @@ module EpubForge
|
|
76
97
|
b.meta :name => "dtb:depth", :content => "1"
|
77
98
|
b.meta :name => "dtb:totalPageCount", :content => "0"
|
78
99
|
b.meta :name => "dtb:maxPageNumber", :content=> "0"
|
100
|
+
|
79
101
|
end
|
80
102
|
|
81
103
|
# <docTitle>
|
@@ -98,7 +120,7 @@ module EpubForge
|
|
98
120
|
b.navLabel do
|
99
121
|
b.text section.title
|
100
122
|
end
|
101
|
-
b.content :src => section.link
|
123
|
+
b.content :src => section.link.relative_path_from( "/OEBPS" )
|
102
124
|
end
|
103
125
|
end
|
104
126
|
end
|
@@ -157,6 +179,11 @@ module EpubForge
|
|
157
179
|
b.dc :publisher, @metadata["publisher"] || "A Pack of Orangutans"
|
158
180
|
b.dc :date, {:"opf:event" => "original-publication"}, @metadata["original-publication"] || Time.now.year
|
159
181
|
b.dc :date, {:"opf:event" => "epub-publication"}, @metadata["epub-publication"] || Time.now.year
|
182
|
+
|
183
|
+
|
184
|
+
if @sections.first.cover
|
185
|
+
b.meta :name => "cover", :content => @sections.first.cover
|
186
|
+
end
|
160
187
|
end
|
161
188
|
|
162
189
|
# <manifest>
|
@@ -165,22 +192,22 @@ module EpubForge
|
|
165
192
|
b.item :id => "ncx", :href => "toc.ncx", :"media-type" => "application/x-dtbncx+xml"
|
166
193
|
|
167
194
|
@sections.each_with_index do |section, i|
|
168
|
-
b.item :id => section.link, :href => section.link, :"media-type" => section.media_type
|
195
|
+
b.item :id => section.link.basename, :href => section.link.relative_path_from("/OEBPS"), :"media-type" => section.media_type
|
169
196
|
end
|
170
197
|
|
171
198
|
@images.each do |image|
|
172
|
-
b.item :id => image.
|
199
|
+
b.item :id => image.filename.basename, :href => image.link.relative_path_from("/OEBPS"), :"media-type" => image.media_type
|
173
200
|
end
|
174
201
|
|
175
202
|
@stylesheets.each do |sheet|
|
176
|
-
b.item :id => sheet.name, :href => sheet.link, :"media-type" => sheet.media_type
|
203
|
+
b.item :id => sheet.name, :href => sheet.link.relative_path_from("/OEBPS"), :"media-type" => sheet.media_type
|
177
204
|
end
|
178
205
|
end
|
179
206
|
|
180
207
|
# <spine toc="ncx">
|
181
208
|
b.spine :toc => "ncx" do
|
182
|
-
@sections.
|
183
|
-
b.itemref :idref =>
|
209
|
+
@sections.each do |section|
|
210
|
+
b.itemref :idref => section.dest_filename
|
184
211
|
end
|
185
212
|
end
|
186
213
|
|
@@ -196,7 +223,7 @@ module EpubForge
|
|
196
223
|
|
197
224
|
b.target!.to_s
|
198
225
|
end
|
199
|
-
|
226
|
+
|
200
227
|
# zips up contents
|
201
228
|
def build
|
202
229
|
Utils::DirectoryBuilder.create( @scratch_dir ) do |build|
|
@@ -212,8 +239,10 @@ module EpubForge
|
|
212
239
|
build.file( "content.opf", content_opf )
|
213
240
|
|
214
241
|
build.dir( "Text" ) do
|
215
|
-
@sections.
|
216
|
-
build.file(
|
242
|
+
@sections.each do |section|
|
243
|
+
build.file( section.dest_filename,
|
244
|
+
section.is_a?( Assets::XHTML ) ? section.html : wrap_page( section.html )
|
245
|
+
)
|
217
246
|
end
|
218
247
|
end
|
219
248
|
|
@@ -245,7 +274,7 @@ module EpubForge
|
|
245
274
|
end
|
246
275
|
|
247
276
|
protected
|
248
|
-
def wrap_page content
|
277
|
+
def wrap_page content = ""
|
249
278
|
b = XmlBuilder::XmlMarkup.new( :indent => 2)
|
250
279
|
b.instruct! :xml, :encoding => "utf-8", :standalone => "no"
|
251
280
|
b.declare! :DOCTYPE, :html, :PUBLIC, "-//W3C//DTD XHTML 1.1//EN", "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"
|
@@ -254,7 +283,7 @@ module EpubForge
|
|
254
283
|
b.head do
|
255
284
|
b.title( @metadata["name"] )
|
256
285
|
for sheet in @stylesheets
|
257
|
-
b.link :href => sheet.link, :media => "screen", :rel => "stylesheet", :type => "text/css"
|
286
|
+
b.link :href => sheet.link.relative_path_from("/OEBPS/Text"), :media => "screen", :rel => "stylesheet", :type => "text/css"
|
258
287
|
end
|
259
288
|
end
|
260
289
|
|
data/lib/epubforge.rb
CHANGED
@@ -4,6 +4,7 @@
|
|
4
4
|
# require 'singleton'
|
5
5
|
# require 'builder'
|
6
6
|
require 'thor'
|
7
|
+
require 'optparse'
|
7
8
|
require 'xdg' # keep configuration files in sane places
|
8
9
|
require 'debugger'
|
9
10
|
require 'erb'
|
@@ -15,7 +16,6 @@ require 'net/http'
|
|
15
16
|
require 'open-uri' # needed by Utils::Downloader
|
16
17
|
require 'yaml'
|
17
18
|
require 'rbconfig'
|
18
|
-
# require 'configurator'
|
19
19
|
require 'fun_with_files'
|
20
20
|
require 'fun_with_configurations'
|
21
21
|
|
@@ -90,6 +90,7 @@ require_relative 'epub/assets/markdown'
|
|
90
90
|
require_relative 'epub/assets/textile'
|
91
91
|
require_relative 'epub/assets/image'
|
92
92
|
require_relative 'epub/assets/stylesheet'
|
93
|
+
require_relative 'epub/assets/xhtml'
|
93
94
|
require_relative 'project/project'
|
94
95
|
|
95
96
|
puts "Requirements loaded" if debugging?
|
data/lib/utils/class_loader.rb
CHANGED
data/lib/utils/file_orderer.rb
CHANGED
@@ -5,7 +5,7 @@ module EpubForge
|
|
5
5
|
# by matching the filenames against one regex after another. Allows you to say,
|
6
6
|
# "I want the title page, the foreward, then all the chapters, then all the appendixes, then the afterword."
|
7
7
|
# Ex: FileOrderer( ["title_page", "forward", "chapter-.*", "afterword", "appendix.*" ).reorder( pages )
|
8
|
-
# Only compares the basename minus extension. Files should come from the same directory
|
8
|
+
# Only compares the basename minus extension. Files should come from the same directory. cover.extension always comes first.
|
9
9
|
class FileOrderer
|
10
10
|
def initialize( matchers )
|
11
11
|
@matchers = matchers.map do |m|
|
@@ -17,26 +17,26 @@ module EpubForge
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
+
@matchers.unshift( /^cover$/ )
|
20
21
|
@matchers.push( /^.*$/ )
|
21
22
|
end
|
22
23
|
|
23
24
|
def reorder( files )
|
24
25
|
files = files.map(&:fwf_filepath)
|
25
26
|
|
26
|
-
files.sort_by!{ |f|
|
27
|
-
f.basename_no_ext.to_s
|
28
|
-
}
|
29
|
-
|
30
|
-
|
31
27
|
ordered_files = @matchers.inject( [] ) do |collector, matcher|
|
32
28
|
matched_files = files.select do |f|
|
33
29
|
name = f.basename_no_ext.to_s
|
34
30
|
matcher.match( name )
|
35
31
|
end
|
36
|
-
|
37
|
-
|
32
|
+
|
33
|
+
matched_files.sort_by! do |filename|
|
34
|
+
filename.basename_no_ext.to_s
|
35
|
+
end
|
36
|
+
|
37
|
+
collector += matched_files.sort
|
38
38
|
files -= matched_files
|
39
|
-
|
39
|
+
|
40
40
|
collector
|
41
41
|
end
|
42
42
|
end
|
data/lib/utils/file_path.rb
CHANGED
@@ -129,7 +129,7 @@ module EpubForge
|
|
129
129
|
self.basename.to_s.split(".").last || ""
|
130
130
|
end
|
131
131
|
|
132
|
-
def
|
132
|
+
def relative_path_from( ancestor_dir )
|
133
133
|
depth = ancestor_dir.to_s.split(File::SEPARATOR).length
|
134
134
|
relative_path = self.to_s.split(File::SEPARATOR)
|
135
135
|
relative_path[(depth)..-1].join(File::SEPARATOR).fwf_filepath
|
@@ -74,7 +74,7 @@ module EpubForge
|
|
74
74
|
def translate( filename, opts = "" )
|
75
75
|
return false unless can_do_job?( filename )
|
76
76
|
|
77
|
-
result =
|
77
|
+
result = ""
|
78
78
|
if @custom_proc
|
79
79
|
result += @custom_proc.call( filename, *opts )
|
80
80
|
elsif @cmd
|
@@ -88,6 +88,7 @@ module EpubForge
|
|
88
88
|
return false
|
89
89
|
end
|
90
90
|
|
91
|
+
result += "\n\n<!-- generated from #{@format} by htmlizer #{@name} -->\n"
|
91
92
|
result
|
92
93
|
end
|
93
94
|
|
data/lib/utils/htmlizer.rb
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
2
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
3
|
+
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
4
|
+
|
5
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
6
|
+
<head>
|
7
|
+
<title></title>
|
8
|
+
</head>
|
9
|
+
|
10
|
+
<body>
|
11
|
+
<p><img class="cover" alt="The Improbable Rise of Singularity Girl, by Bryce Anderson" src="../Images/cover.jpg" /></p>
|
12
|
+
</body>
|
13
|
+
</html>
|
Binary file
|
@@ -42,7 +42,7 @@ FunWith::Configurations::Config.new do
|
|
42
42
|
# chapter_summary that comes after, you might want to define the matcher more
|
43
43
|
# specifically, for example 'chapter-\d+' (chapter followed by dash followed by any number of numbers).
|
44
44
|
#
|
45
|
-
"chapter
|
45
|
+
"chapter-.*",
|
46
46
|
"afterword"
|
47
47
|
]
|
48
48
|
|
data/test/test_epubforge.rb
CHANGED
@@ -7,7 +7,7 @@ class TestEpubforge < Test::Unit::TestCase #
|
|
7
7
|
printout = EpubForge.collect_stdout do
|
8
8
|
EpubForge::Action::Runner.new.exec # empty args, should append --help
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
assert_match /\( wc \| count \)/, printout
|
12
12
|
assert_match /epubforge \[action\] \[folder\]/, printout
|
13
13
|
end
|
@@ -27,10 +27,10 @@ class TestEpubforge < Test::Unit::TestCase #
|
|
27
27
|
should "successfully count ALL THE WORDS!" do
|
28
28
|
create_project do
|
29
29
|
EpubForge.collect_stdout do
|
30
|
-
report = EpubForge::Action::Runner.new.exec( "wc", @project_dir )
|
30
|
+
report = EpubForge::Action::Runner.new.exec( "wc", @project_dir ).execution_returned
|
31
31
|
assert_kind_of Hash, report
|
32
|
-
assert_equal
|
33
|
-
assert_equal
|
32
|
+
assert_equal 119, report["Book"]
|
33
|
+
assert_equal 126, report["Today"]
|
34
34
|
assert @project_dir.join( EpubForge::Project::SETTINGS_FOLDER, EpubForge::Action::WordCount::WORD_COUNT_FILE ).exist?
|
35
35
|
end
|
36
36
|
end
|
@@ -39,7 +39,9 @@ class TestEpubforge < Test::Unit::TestCase #
|
|
39
39
|
should "fail to count words when no project is given and cwd is not a project" do
|
40
40
|
create_project do
|
41
41
|
printout = EpubForge.collect_stdout do
|
42
|
-
|
42
|
+
run_description = EpubForge::Action::Runner.new.exec( "wc" )
|
43
|
+
assert_equal nil, run_description.execution_returned
|
44
|
+
assert_equal false, run_description.success?
|
43
45
|
end
|
44
46
|
|
45
47
|
assert_match /Error\(s\) trying to complete the requested action/, printout
|
@@ -74,8 +76,8 @@ class TestEpubforge < Test::Unit::TestCase #
|
|
74
76
|
end
|
75
77
|
|
76
78
|
d.join("Text") do |d|
|
77
|
-
d.join( "
|
78
|
-
assert section.file
|
79
|
+
d.join( "afterword.xhtml" ) do |section|
|
80
|
+
assert section.file?, "file should exist: chapter-0002.xhtml"
|
79
81
|
section_text = section.read
|
80
82
|
assert_match /DOCTYPE/, section_text
|
81
83
|
assert_match /<title>#{@book_title}<\/title>/, section_text
|
@@ -90,7 +92,7 @@ class TestEpubforge < Test::Unit::TestCase #
|
|
90
92
|
d.join( "content.opf" ) do |content|
|
91
93
|
content_text = content.read
|
92
94
|
assert_match /<dc:title>#{@book_title}<\/dc:title>/, content_text
|
93
|
-
assert_match /Text\/
|
95
|
+
assert_match /Text\/chapter-0003/, content_text
|
94
96
|
end
|
95
97
|
|
96
98
|
d.join( "toc.ncx" ) do |toc|
|
@@ -128,7 +130,6 @@ class TestEpubforge < Test::Unit::TestCase #
|
|
128
130
|
|
129
131
|
assert @project_dir.directory?, "Project directory doesn't exist. Cannot proceed."
|
130
132
|
|
131
|
-
|
132
133
|
@book_title = fill_in_project_options[:answers][:title]
|
133
134
|
@chapter_count = fill_in_project_options[:answers][:chapter_count].to_i
|
134
135
|
@ebook_file = @project_dir.join( @book_title.epf_underscorize + ".epub" )
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: epubforge
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-05-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: xdg
|
@@ -223,6 +223,7 @@ files:
|
|
223
223
|
- ./config/actions/help.rb
|
224
224
|
- ./config/actions/init.rb
|
225
225
|
- ./config/actions/kindle.rb
|
226
|
+
- ./config/actions/mobify.rb
|
226
227
|
- ./config/actions/notes_to_epub.rb
|
227
228
|
- ./config/actions/notes_to_kindle.rb
|
228
229
|
- ./config/actions/word_count.rb
|
@@ -248,6 +249,7 @@ files:
|
|
248
249
|
- ./lib/epub/assets/page.rb
|
249
250
|
- ./lib/epub/assets/stylesheet.rb
|
250
251
|
- ./lib/epub/assets/textile.rb
|
252
|
+
- ./lib/epub/assets/xhtml.rb
|
251
253
|
- ./lib/epub/builder.rb
|
252
254
|
- ./lib/epub/packager.rb
|
253
255
|
- ./lib/epubforge.rb
|
@@ -268,11 +270,13 @@ files:
|
|
268
270
|
- ./lib/utils/template_evaluator.rb
|
269
271
|
- ./templates/default/book/afterword.markdown.template
|
270
272
|
- ./templates/default/book/chapter-%i%.markdown.sequence
|
273
|
+
- ./templates/default/book/cover.xhtml.template
|
271
274
|
- ./templates/default/book/foreword.markdown.template
|
272
275
|
- ./templates/default/book/images/cover.png
|
273
276
|
- ./templates/default/book/stylesheets/stylesheet.css.template
|
274
277
|
- ./templates/default/book/title_page.markdown.template
|
275
278
|
- ./templates/default/notes/character.named.markdown.template
|
279
|
+
- ./templates/default/notes/images/cover.png
|
276
280
|
- ./templates/default/notes/stylesheets/stylesheet.css.template
|
277
281
|
- ./templates/default/payload.rb
|
278
282
|
- ./templates/default/settings/actions/local_action.rb.example
|
@@ -311,7 +315,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
311
315
|
version: '0'
|
312
316
|
segments:
|
313
317
|
- 0
|
314
|
-
hash:
|
318
|
+
hash: 243230996870064853
|
315
319
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
316
320
|
none: false
|
317
321
|
requirements:
|