epub-maker 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/CHANGELOG.markdown +4 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.markdown +154 -0
- data/Rakefile +11 -0
- data/bin/epub-maker +3 -0
- data/epub-maker.gemspec +41 -0
- data/lib/epub/maker.rb +113 -0
- data/lib/epub/maker/content_document.rb +36 -0
- data/lib/epub/maker/ocf.rb +60 -0
- data/lib/epub/maker/publication.rb +340 -0
- data/lib/epub/maker/task.rb +134 -0
- data/lib/epub/maker/version.rb +5 -0
- data/test/fixtures/book/META-INF/container.xml +6 -0
- data/test/fixtures/book/OPS/impl.xhtml +9 -0
- data/test/fixtures/book/OPS/item-1.xhtml +8 -0
- data/test/fixtures/book/OPS/item-2.xhtml +12 -0
- data/test/fixtures/book/OPS/nav.xhtml +26 -0
- data/test/fixtures/book/OPS/slideshow.xml +0 -0
- data/test/fixtures/book/OPS//343/203/253/343/203/274/343/203/210/343/203/225/343/202/241/343/202/244/343/203/253.opf +91 -0
- data/test/helper.rb +17 -0
- data/test/make_task.rake +24 -0
- data/test/schemas/epub-nav-30.rnc +10 -0
- data/test/schemas/epub-nav-30.sch +72 -0
- data/test/schemas/epub-xhtml-30.sch +377 -0
- data/test/schemas/ocf-container-30.rnc +16 -0
- data/test/test_inplace_editing.rb +62 -0
- data/test/test_maker.rb +66 -0
- data/test/test_maker_ocf.rb +61 -0
- data/test/test_maker_publication.rb +40 -0
- data/test/test_task.rb +56 -0
- metadata +333 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 151508171923e54e7fff1950f2faceedab975568
|
4
|
+
data.tar.gz: 1343fe1b022341d99765ebc1a46a9261fb0339c7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6261ffcbc75632d75188ee109c544d46955a9917f3b817a8dd2df8d0edceca530ee4b436bf4347e14a7d6d3f9610333eed8420da22d608da78b4f8357f9ce6b8
|
7
|
+
data.tar.gz: de1a958a0ec211d11e82eac5f3e59bcde89b84da93bb5cb0faadc9f1a4b48ec8ef94551ff44c18edc715dff7a89fa060b7430108ba3b48c5f994aa01c4d6fd41
|
data/.gitignore
ADDED
data/CHANGELOG.markdown
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012, 2013 KITAITI Makoto
|
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.
|
data/README.markdown
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
EPUB Maker
|
2
|
+
==========
|
3
|
+
|
4
|
+
This library supports making and editing EPUB books
|
5
|
+
|
6
|
+
Installation
|
7
|
+
------------
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'epub-maker'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install epub-maker
|
20
|
+
|
21
|
+
Usage
|
22
|
+
-----
|
23
|
+
|
24
|
+
### Library ###
|
25
|
+
|
26
|
+
require 'epub/maker'
|
27
|
+
|
28
|
+
EPUB::Maker.make path/to/generated/book.epub do |book|
|
29
|
+
book.make_ocf do |ocf|
|
30
|
+
ocf.make_container do |container|
|
31
|
+
container.make_rootfile full_path: 'OPS/content.opf'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
book.make_package do |package|
|
36
|
+
package.dir = 'rtl'
|
37
|
+
|
38
|
+
package.make_metadata do |metadata|
|
39
|
+
metadata.title = 'Sample eBook'
|
40
|
+
metadata.language = 'ja'
|
41
|
+
end
|
42
|
+
|
43
|
+
package.make_manifest do |manifest|
|
44
|
+
Pathname.glob("#{@fixture_dir}/OPS/*.{xhtml,xml}").each_with_index do |content_path, index|
|
45
|
+
manifest.make_item do |item|
|
46
|
+
item.id = "item-#{index + 1}"
|
47
|
+
href = content_path.relative_path_from(@fixture_dir + File.dirname(book.rootfile_path))
|
48
|
+
item.href = Addressable::URI.parse(href.to_s)
|
49
|
+
item.media_type = case content_path.extname
|
50
|
+
when '.xhtml' then 'application/xhtml+xml'
|
51
|
+
when '.png' then 'image/png'
|
52
|
+
end
|
53
|
+
item.content_file = (@fixture_dir + item.entry_name).to_path
|
54
|
+
item.properties << 'nav' if content_path.basename.to_path == 'nav.xhtml'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
package.make_spine do |spine|
|
59
|
+
spine.page_progression_direction = 'rtl'
|
60
|
+
|
61
|
+
package.manifest.items.select(&:xhtml?).each do |item|
|
62
|
+
spine.make_itemref do |itemref|
|
63
|
+
itemref.item = item
|
64
|
+
itemref.linear = true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
For structure of EPUB book, see [EPUB Parser's documentation][epub-parser-doc].
|
73
|
+
|
74
|
+
### Rake task ###
|
75
|
+
|
76
|
+
**CAUTION**: Still work in progress. File path to require and API will be modified in the future.
|
77
|
+
|
78
|
+
require 'epub/maker/task'
|
79
|
+
|
80
|
+
DIR = 'path/to/dir/holding/contents'
|
81
|
+
EPUB::Maker::Task.new @epub_name do |task|
|
82
|
+
task.titles = ['EPUB Maker Rake Example']
|
83
|
+
|
84
|
+
task.base_dir = DIR
|
85
|
+
|
86
|
+
task.files.include "#{task.base_dir}/**/*"
|
87
|
+
task.files.exclude {|entry| ! File.file? entry}
|
88
|
+
|
89
|
+
task.rootfile = "#{DIR}/OPS/contents.opf"
|
90
|
+
task.make_rootfiles = true
|
91
|
+
|
92
|
+
task.resources = task.files.dup
|
93
|
+
task.resources.exclude /\.opf/
|
94
|
+
task.resources.exclude /META\-INF/
|
95
|
+
|
96
|
+
task.navs.include 'OPS/nav.xhtml'
|
97
|
+
task.media_types = {"#{DIR}/OPS/slideshow.xml" => 'application/x-demo-slideshow'}
|
98
|
+
|
99
|
+
task.spine = task.resources.dup
|
100
|
+
task.spine.exclude /OPS\/impl\.xhtml\z/
|
101
|
+
task.spine.exclude /\.xml\z/
|
102
|
+
|
103
|
+
task.bindings = {'application/x-demo-slideshow' => "#{DIR}/OPS/impl.xhtml"}
|
104
|
+
end
|
105
|
+
|
106
|
+
### In-place editing
|
107
|
+
|
108
|
+
require 'epub/maker'
|
109
|
+
|
110
|
+
book = EPUB::Parser.parse('path/to/book.epub')
|
111
|
+
book.resources.select(&:xhtml?).each do |item|
|
112
|
+
doc = item.content_document.nokogiri
|
113
|
+
title = doc/'title'
|
114
|
+
title.content += ' - Additional Title such like book title'
|
115
|
+
item.content = doc.to_xml
|
116
|
+
item.save
|
117
|
+
end
|
118
|
+
|
119
|
+
Shortcut:
|
120
|
+
|
121
|
+
book.resources.select(&:xhtml?).each do |item|
|
122
|
+
item.edit_with_nokogiri do |doc| # Nokogiri::XML::Document is passed to block
|
123
|
+
doc.search('img').each do |img|
|
124
|
+
img['alt'] = '' if img['alt'].nil?
|
125
|
+
end
|
126
|
+
end # item.content = doc.to_xml is called automatically
|
127
|
+
end # item.save is called automatically
|
128
|
+
|
129
|
+
For APIs of parsed EPUB book, see [EPUB Parser's documentation][epub-parser-doc].
|
130
|
+
|
131
|
+
[epub-parser-doc]: http://rubydoc.info/gems/epub-parser/frames
|
132
|
+
|
133
|
+
Requirements
|
134
|
+
------------
|
135
|
+
* Ruby 2.0 or later
|
136
|
+
* C compiler to build zipruby and Nokogiri gems
|
137
|
+
|
138
|
+
Todo
|
139
|
+
----
|
140
|
+
* Refine Rake task
|
141
|
+
|
142
|
+
Recent Changes
|
143
|
+
--------------
|
144
|
+
### 0.0.1
|
145
|
+
* Initial release!
|
146
|
+
|
147
|
+
Contributing
|
148
|
+
------------
|
149
|
+
|
150
|
+
1. Fork it
|
151
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
152
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
153
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
154
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/epub-maker
ADDED
data/epub-maker.gemspec
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'epub/maker/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "epub-maker"
|
8
|
+
gem.version = EPUB::Maker::VERSION
|
9
|
+
gem.authors = ["KITAITI Makoto"]
|
10
|
+
gem.email = ["KitaitiMakoto@gmail.com"]
|
11
|
+
gem.description = %q{This library supports making and editing EPUB books}
|
12
|
+
gem.summary = %q{EPUB Maker}
|
13
|
+
gem.homepage = ""
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.files.delete '"test/fixtures/book/OPS/\343\203\253\343\203\274\343\203\210\343\203\225\343\202\241\343\202\244\343\203\253.opf"'
|
17
|
+
gem.files.push('test/fixtures/book/OPS/ルートファイル.opf')
|
18
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
19
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
20
|
+
gem.require_paths = ["lib"]
|
21
|
+
gem.required_ruby_version = '>= 2.0.0'
|
22
|
+
|
23
|
+
gem.add_runtime_dependency 'zipruby'
|
24
|
+
gem.add_runtime_dependency 'epub-parser', '>= 0.1.5'
|
25
|
+
gem.add_runtime_dependency 'pathname-common_prefix'
|
26
|
+
gem.add_runtime_dependency 'mime-types'
|
27
|
+
gem.add_runtime_dependency 'ruby-uuid'
|
28
|
+
gem.add_runtime_dependency 'archive-zip'
|
29
|
+
gem.add_runtime_dependency 'rake'
|
30
|
+
|
31
|
+
gem.add_development_dependency 'test-unit-full'
|
32
|
+
gem.add_development_dependency 'epubcheck'
|
33
|
+
gem.add_development_dependency 'epzip'
|
34
|
+
gem.add_development_dependency 'simplecov'
|
35
|
+
gem.add_development_dependency 'pry'
|
36
|
+
gem.add_development_dependency 'pry-doc'
|
37
|
+
gem.add_development_dependency 'yard'
|
38
|
+
gem.add_development_dependency 'redcarpet'
|
39
|
+
gem.add_development_dependency 'gem-man'
|
40
|
+
gem.add_development_dependency 'ronn'
|
41
|
+
end
|
data/lib/epub/maker.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'pathname/common_prefix'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'tmpdir'
|
5
|
+
require 'time'
|
6
|
+
require 'uuid'
|
7
|
+
require 'archive/zip'
|
8
|
+
require 'epub'
|
9
|
+
require 'epub/constants'
|
10
|
+
require 'epub/book'
|
11
|
+
require 'epub/parser'
|
12
|
+
require "epub/maker/version"
|
13
|
+
require 'epub/maker/ocf'
|
14
|
+
require 'epub/maker/publication'
|
15
|
+
require 'epub/maker/content_document'
|
16
|
+
|
17
|
+
module EPUB
|
18
|
+
module Maker
|
19
|
+
class << self
|
20
|
+
# @param path [Pathname|#to_path|String]
|
21
|
+
# @todo Add option whether mv blocks or not when file locked already
|
22
|
+
# @todo Timeout when file shared-locked long time
|
23
|
+
def make(path)
|
24
|
+
path = Pathname(path) unless path.kind_of? Pathname
|
25
|
+
book = EPUB::Book.new
|
26
|
+
Pathname.mktmpdir 'epub-maker' do |dir|
|
27
|
+
temp_path = dir/path.basename
|
28
|
+
mimetype = dir/'mimetype'
|
29
|
+
mimetype.write EPUB::MediaType::EPUB
|
30
|
+
Archive::Zip.open temp_path.to_path, :w do |archive|
|
31
|
+
file = Archive::Zip::Entry.from_file(mimetype.to_path, compression_codec: Archive::Zip::Codec::Store)
|
32
|
+
archive.add_entry file
|
33
|
+
end
|
34
|
+
|
35
|
+
Zip::Archive.open temp_path.to_path do |archive|
|
36
|
+
yield book if block_given?
|
37
|
+
book.save archive
|
38
|
+
end
|
39
|
+
|
40
|
+
path.open 'wb' do |file|
|
41
|
+
raise "File locked by other process: #{path}" unless file.flock File::LOCK_SH|File::LOCK_NB
|
42
|
+
($VERBOSE ? ::FileUtils::Verbose : ::FileUtils).move temp_path.to_path, path.to_path
|
43
|
+
end
|
44
|
+
end
|
45
|
+
book
|
46
|
+
|
47
|
+
# validate
|
48
|
+
# build_xml
|
49
|
+
# archive
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def make_ocf
|
55
|
+
self.ocf = OCF.new
|
56
|
+
ocf.make do |ocf|
|
57
|
+
yield ocf if block_given?
|
58
|
+
end
|
59
|
+
ocf
|
60
|
+
end
|
61
|
+
|
62
|
+
def make_package
|
63
|
+
self.package = Publication::Package.new
|
64
|
+
package.make do |package|
|
65
|
+
yield package if block_given?
|
66
|
+
end
|
67
|
+
package
|
68
|
+
end
|
69
|
+
|
70
|
+
# @param archive [Zip::Archive]
|
71
|
+
def save(archive)
|
72
|
+
ocf.save archive
|
73
|
+
package.save archive
|
74
|
+
resources.each do |item|
|
75
|
+
item.save archive
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class Pathname
|
81
|
+
class << self
|
82
|
+
# @overload mktmpdir(prefix_suffix=nil, tmpdir=nil)
|
83
|
+
# @param prefix_suffix [String|nil] see Dir.mktmpdir
|
84
|
+
# @param tmpdir [String|nil] see Dir.mktmpdir
|
85
|
+
# @return [Pathname] path to temporary directory
|
86
|
+
# @overload mktmpdir(prefix_suffix=nil, tmpdir=nil)
|
87
|
+
# @param prefix_suffix [String|nil] see Dir.mktmpdir
|
88
|
+
# @param tmpdir [String|nil] see Dir.mktmpdir
|
89
|
+
# @yieldparam dir [Pathname] path to temporary directory
|
90
|
+
# @return value of given block
|
91
|
+
def mktmpdir(prefix_suffix=nil, tmpdir=nil)
|
92
|
+
if block_given?
|
93
|
+
Dir.mktmpdir prefix_suffix, tmpdir do |dir|
|
94
|
+
yield new(dir)
|
95
|
+
end
|
96
|
+
else
|
97
|
+
new(Dir.mktmpdir(prefix_suffix, tmpdir))
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def write(string, mode='w', perm=0666)
|
103
|
+
open mode, perm do |file|
|
104
|
+
file << string
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def remove_entry_secure
|
109
|
+
FileUtils.remove_entry_secure to_path
|
110
|
+
end
|
111
|
+
|
112
|
+
alias / +
|
113
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'epub/content_document'
|
2
|
+
|
3
|
+
module EPUB
|
4
|
+
module ContentDocument
|
5
|
+
class Navigation
|
6
|
+
def to_xml(options={:encoding => 'UTF-8'})
|
7
|
+
Nokogiri::XML::Builder.new(options) {|xml|
|
8
|
+
xml.html('xmlns' => EPUB::NAMESPACES['xhtml'], 'xmlns:epub' => EPUB::NAMESPACES['epub']) {
|
9
|
+
xml.head {
|
10
|
+
xml.title_ 'Table of Contents'
|
11
|
+
}
|
12
|
+
xml.body {
|
13
|
+
navigations.each do |navigation|
|
14
|
+
xml.nav_('epub:type' => navigation.type) {
|
15
|
+
unless navigation.items.empty?
|
16
|
+
xml.ol {
|
17
|
+
navigation.items.each do |item|
|
18
|
+
xml.li {
|
19
|
+
if item.href
|
20
|
+
xml.a item.text, 'href' => item.href
|
21
|
+
else
|
22
|
+
xml.span_ item.text
|
23
|
+
end
|
24
|
+
}
|
25
|
+
end
|
26
|
+
}
|
27
|
+
end
|
28
|
+
}
|
29
|
+
end
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}.to_xml
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'epub/ocf'
|
2
|
+
|
3
|
+
# @todo Use refinement
|
4
|
+
module EPUB
|
5
|
+
class OCF
|
6
|
+
DIRECTORY = 'META-INF'
|
7
|
+
|
8
|
+
def make
|
9
|
+
yield self if block_given?
|
10
|
+
self
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param archive [Zip::Archive] path to archive file
|
14
|
+
def save(archive)
|
15
|
+
self.container.save archive if self.container
|
16
|
+
end
|
17
|
+
|
18
|
+
# @overload make_container
|
19
|
+
# @return [Container]
|
20
|
+
# @overload make_container
|
21
|
+
# @return [Container]
|
22
|
+
# @yield [container] if block given
|
23
|
+
# @yieldparam [Container]
|
24
|
+
def make_container
|
25
|
+
self.container = Container.new
|
26
|
+
yield container if block_given?
|
27
|
+
container
|
28
|
+
end
|
29
|
+
|
30
|
+
class Container
|
31
|
+
def to_xml(options={:encoding => 'UTF-8'})
|
32
|
+
Nokogiri::XML::Builder.new(options) {|xml|
|
33
|
+
xml.container('xmlns' => EPUB::NAMESPACES['ocf'], 'version' => '1.0') {
|
34
|
+
xml.rootfiles {
|
35
|
+
rootfiles.each do |rootfile|
|
36
|
+
xml.rootfile('full-path' => rootfile.full_path,
|
37
|
+
'media-type' => rootfile.media_type)
|
38
|
+
end
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}.to_xml
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param archive [Zip::Archive]
|
45
|
+
def save(archive)
|
46
|
+
archive.add_buffer File.join(DIRECTORY, Container::FILE), to_xml
|
47
|
+
end
|
48
|
+
|
49
|
+
# @option full_path [String|nil] full path to package document file in container such like "OPS/content.opf"
|
50
|
+
# @option media_type [String] media type
|
51
|
+
# @yield [Rootfile] rootfile
|
52
|
+
def make_rootfile(full_path: nil, media_type: EPUB::MediaType::ROOTFILE)
|
53
|
+
rootfile = Rootfile.new(full_path, media_type)
|
54
|
+
@rootfiles << rootfile
|
55
|
+
yield rootfile if block_given?
|
56
|
+
rootfile
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|