bookbinder 0.2.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/README.md +97 -0
- data/Rakefile +12 -0
- data/bin/bookbinder +17 -0
- data/lib/bookbinder/document_proxy.rb +171 -0
- data/lib/bookbinder/file.rb +149 -0
- data/lib/bookbinder/file_system/directory.rb +62 -0
- data/lib/bookbinder/file_system/memory.rb +57 -0
- data/lib/bookbinder/file_system/zip_file.rb +106 -0
- data/lib/bookbinder/file_system.rb +35 -0
- data/lib/bookbinder/media_type.rb +17 -0
- data/lib/bookbinder/operations.rb +59 -0
- data/lib/bookbinder/package/epub.rb +69 -0
- data/lib/bookbinder/package/openbook.rb +33 -0
- data/lib/bookbinder/package.rb +295 -0
- data/lib/bookbinder/transform/epub/audio_overlay.rb +227 -0
- data/lib/bookbinder/transform/epub/audio_soundtrack.rb +73 -0
- data/lib/bookbinder/transform/epub/contributor.rb +11 -0
- data/lib/bookbinder/transform/epub/cover_image.rb +80 -0
- data/lib/bookbinder/transform/epub/cover_page.rb +148 -0
- data/lib/bookbinder/transform/epub/creator.rb +67 -0
- data/lib/bookbinder/transform/epub/description.rb +43 -0
- data/lib/bookbinder/transform/epub/language.rb +29 -0
- data/lib/bookbinder/transform/epub/metadata.rb +140 -0
- data/lib/bookbinder/transform/epub/nav.rb +60 -0
- data/lib/bookbinder/transform/epub/nav_toc.rb +177 -0
- data/lib/bookbinder/transform/epub/ncx.rb +63 -0
- data/lib/bookbinder/transform/epub/ocf.rb +33 -0
- data/lib/bookbinder/transform/epub/opf.rb +22 -0
- data/lib/bookbinder/transform/epub/package_identifier.rb +87 -0
- data/lib/bookbinder/transform/epub/rendition.rb +265 -0
- data/lib/bookbinder/transform/epub/resources.rb +38 -0
- data/lib/bookbinder/transform/epub/spine.rb +79 -0
- data/lib/bookbinder/transform/epub/title.rb +92 -0
- data/lib/bookbinder/transform/epub/version.rb +39 -0
- data/lib/bookbinder/transform/generator.rb +8 -0
- data/lib/bookbinder/transform/openbook/json.rb +15 -0
- data/lib/bookbinder/transform/organizer.rb +41 -0
- data/lib/bookbinder/transform.rb +7 -0
- data/lib/bookbinder/version.rb +5 -0
- data/lib/bookbinder.rb +29 -0
- metadata +131 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
class Bookbinder::Transform::EPUB_Spine < Bookbinder::Transform
|
2
|
+
|
3
|
+
def dependencies
|
4
|
+
[Bookbinder::Transform::EPUB_Resources]
|
5
|
+
end
|
6
|
+
|
7
|
+
|
8
|
+
def to_map(package)
|
9
|
+
opf_doc = package.file(:opf).document('r')
|
10
|
+
itemrefs = opf_doc.search('opf|spine > opf|itemref')
|
11
|
+
package.map['spine'] = itemrefs.collect { |itemref|
|
12
|
+
cmpt = package.map['resources'].detect { |r| r['id'] == itemref['idref'] }
|
13
|
+
if cmpt
|
14
|
+
package.map['resources'].delete(cmpt)
|
15
|
+
cmpt['linear'] = itemref['linear'] == 'no' ? false : true
|
16
|
+
cmpt
|
17
|
+
else
|
18
|
+
package.warn("No manifest item for spine idref: #{itemref['idref']}")
|
19
|
+
end
|
20
|
+
}.compact
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def from_map(package)
|
25
|
+
opf_doc = package.file(:opf).document
|
26
|
+
|
27
|
+
package.map['spine'].each { |cmpt|
|
28
|
+
# Convert component to valid XHTML
|
29
|
+
cmpt_file = package.file(cmpt['path'])
|
30
|
+
cmpt_doc = cmpt_file.document
|
31
|
+
# Add the EPUB namespace:
|
32
|
+
# FIXME: only add the EPUB namespace if there are any epub:* attrs?
|
33
|
+
cmpt_doc.add_namespace('epub')
|
34
|
+
|
35
|
+
# Update the OPF manifest
|
36
|
+
opf_doc.new_node('item', :append => 'opf|manifest') { |manifest_item_tag|
|
37
|
+
manifest_item_tag['href'] = package.make_href(cmpt['path'])
|
38
|
+
manifest_item_tag['id'] = cmpt['id']
|
39
|
+
manifest_item_tag['media-type'] = cmpt['media-type']
|
40
|
+
props = component_properties(cmpt, cmpt_doc)
|
41
|
+
manifest_item_tag['properties'] = props.join(' ') if props.any?
|
42
|
+
}
|
43
|
+
|
44
|
+
# Update the OPF spine
|
45
|
+
opf_doc.new_node('itemref', :append => 'opf|spine') { |spine_item_tag|
|
46
|
+
spine_item_tag['idref'] = cmpt['id']
|
47
|
+
spine_item_tag['linear'] = 'no' unless cmpt['linear']
|
48
|
+
}
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
# Assemble the component properties by inspecting the HTML...
|
56
|
+
# because some reading systems can't do this themselves, I guess.
|
57
|
+
#
|
58
|
+
# NB: the 'cover-image' and 'nav' properties will be set in
|
59
|
+
# other tranforms.
|
60
|
+
#
|
61
|
+
def component_properties(cmpt, cmpt_doc)
|
62
|
+
[].tap { |props|
|
63
|
+
# 'scripted': check whether there are any script tags in the component.
|
64
|
+
props.push('scripted') if cmpt_doc.find('script')
|
65
|
+
|
66
|
+
# 'mathml': look for a math element
|
67
|
+
props.push('mathml') if cmpt_doc.find('math, mathml:math')
|
68
|
+
|
69
|
+
# 'remote-resources': TODO
|
70
|
+
|
71
|
+
# 'svg': look for an svg element
|
72
|
+
props.push('svg') if cmpt_doc.find('svg, svg:svg')
|
73
|
+
|
74
|
+
# 'switch': look for an epub:switch element
|
75
|
+
props.push('switch') if cmpt_doc.find('epub|switch')
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# EPUB2 spec:
|
2
|
+
#
|
3
|
+
# http://www.idpf.org/epub/20/spec/OPF_2.0.1_draft.htm#Section2.2.1
|
4
|
+
#
|
5
|
+
# EPUB3 spec:
|
6
|
+
#
|
7
|
+
# http://www.idpf.org/epub/30/spec/epub30-publications.html#sec-opf-dctitle
|
8
|
+
#
|
9
|
+
# On title types:
|
10
|
+
#
|
11
|
+
# "When the title-type value is drawn from a code list or other formal
|
12
|
+
# enumeration, the scheme attribute should be attached to identify its source.
|
13
|
+
# When a scheme is not specified, Reading Systems should recognize the
|
14
|
+
# following title type values: main, subtitle, short, collection, edition
|
15
|
+
# and expanded."
|
16
|
+
#
|
17
|
+
class Bookbinder::Transform::EPUB_Title < Bookbinder::Transform
|
18
|
+
|
19
|
+
TITLE_TYPES = %w[main subtitle short collection edition expanded]
|
20
|
+
|
21
|
+
|
22
|
+
def dependencies
|
23
|
+
[Bookbinder::Transform::EPUB_Metadata, Bookbinder::Transform::EPUB_NCX]
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
def to_map(package)
|
28
|
+
package.map['title'] = titles = {}
|
29
|
+
return unless package.map['metadata']
|
30
|
+
return unless metadata_array = package.map['metadata']['title']
|
31
|
+
title_data = metadata_array.sort { |a, b|
|
32
|
+
if a['display-seq'] && b['display-seq']
|
33
|
+
a['display-seq']['@'].to_i <=> b['display-seq']['@'].to_i
|
34
|
+
else
|
35
|
+
0
|
36
|
+
end
|
37
|
+
}
|
38
|
+
title_data.each { |tdata|
|
39
|
+
t = tdata['@']
|
40
|
+
type = tdata['title-type'] ? tdata['title-type']['@'] : 'main'
|
41
|
+
next unless TITLE_TYPES.include?(type)
|
42
|
+
if titles[type]
|
43
|
+
package.warn("Existing title for '#{type}' - discarding '#{t}'")
|
44
|
+
else
|
45
|
+
titles[type] = t
|
46
|
+
metadata_array.delete(tdata)
|
47
|
+
end
|
48
|
+
}
|
49
|
+
# Now that we have "used" the raw metadata for titles, remove it.
|
50
|
+
package.map['metadata'].delete('title') if metadata_array.empty?
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def from_map(package)
|
55
|
+
titles = package.map['title']
|
56
|
+
opf_doc = package.file(:opf).document
|
57
|
+
metadata_tag = opf_doc.find('opf|metadata')
|
58
|
+
seq = 0
|
59
|
+
titles.each_pair { |type, title|
|
60
|
+
title_tag = opf_doc.new_node('dc:title', :append => metadata_tag)
|
61
|
+
title_tag.content = title
|
62
|
+
if titles.length > 1
|
63
|
+
seq += 1
|
64
|
+
title_id = "dc-title-metadata-#{seq}"
|
65
|
+
title_tag['id'] = title_id
|
66
|
+
opf_doc.new_node('meta', :append => metadata_tag) { |type_meta_tag|
|
67
|
+
type_meta_tag.content = type
|
68
|
+
type_meta_tag['property'] = 'title-type'
|
69
|
+
type_meta_tag['refines'] = '#'+title_id
|
70
|
+
}
|
71
|
+
opf_doc.new_node('meta', :append => metadata_tag) { |seq_meta_tag|
|
72
|
+
seq_meta_tag.content = seq
|
73
|
+
seq_meta_tag['property'] = 'display-seq'
|
74
|
+
seq_meta_tag['refines'] = '#'+title_id
|
75
|
+
}
|
76
|
+
end
|
77
|
+
}
|
78
|
+
|
79
|
+
# Add it to the NCX if that file exists in the package
|
80
|
+
package.if_file(:ncx) { |ncx_file|
|
81
|
+
ncx_title = [titles['main'], titles['subtitle']].compact.join(': ')
|
82
|
+
ncx_doc = ncx_file.document
|
83
|
+
ncx_doc.new_node('docTitle') { |doc_title_tag|
|
84
|
+
ncx_doc.new_node('text', :append => doc_title_tag) { |text_tag|
|
85
|
+
text_tag.content = ncx_title
|
86
|
+
}
|
87
|
+
ncx_doc.find('ncx|head').add_next_sibling(doc_title_tag)
|
88
|
+
}
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# See https://itunesconnect.apple.com/docs/iBooksAssetGuide5.1Revision2.pdf
|
2
|
+
# pages 22 and 23.
|
3
|
+
#
|
4
|
+
# "The version of your book is specified within a `meta` element
|
5
|
+
# in the Package Document. The `meta` element has a property value
|
6
|
+
# of `ibooks:version':
|
7
|
+
#
|
8
|
+
# <meta property="ibooks:version">1.1.2</meta>
|
9
|
+
#
|
10
|
+
class Bookbinder::Transform::EPUB_Version < Bookbinder::Transform
|
11
|
+
|
12
|
+
def dependencies
|
13
|
+
[Bookbinder::Transform::EPUB_Metadata]
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def to_map(package)
|
18
|
+
ver_hashes = package.map['metadata'].delete('ibooks:version')
|
19
|
+
if ver_hashes && ver_hashes.any?
|
20
|
+
package.map['version'] = ver_hashes.first['@']
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def from_map(package)
|
26
|
+
if package.map['version']
|
27
|
+
# Add the ibooks prefix to the package root.
|
28
|
+
opf_doc = package.file(:opf).document
|
29
|
+
opf_doc.add_prefix('ibooks')
|
30
|
+
|
31
|
+
# Create the meta node and append it to <metadata>
|
32
|
+
opf_doc.new_node('meta', :append => 'opf|metadata') { |ver_tag|
|
33
|
+
ver_tag['property'] = 'ibooks:version'
|
34
|
+
ver_tag.content = package.map['version']
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Bookbinder::Transform::Openbook_JSON < Bookbinder::Transform
|
2
|
+
|
3
|
+
JSON_PATH = 'book.json'
|
4
|
+
|
5
|
+
|
6
|
+
def to_map(package)
|
7
|
+
package.map.update(JSON.load(package.file(JSON_PATH)))
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
def from_map(package)
|
12
|
+
package.file(JSON_PATH).write(JSON.pretty_generate(package.mip))
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class Bookbinder::Transform::Organizer < Bookbinder::Transform
|
2
|
+
|
3
|
+
PREFERRED_ORDER = %w(
|
4
|
+
title
|
5
|
+
creator
|
6
|
+
contributor
|
7
|
+
language
|
8
|
+
cover
|
9
|
+
description
|
10
|
+
unique-identifier
|
11
|
+
modified
|
12
|
+
version
|
13
|
+
rendition-*
|
14
|
+
audio-*
|
15
|
+
spine
|
16
|
+
nav
|
17
|
+
resources
|
18
|
+
)
|
19
|
+
|
20
|
+
def to_map(package)
|
21
|
+
new_map = {}
|
22
|
+
PREFERRED_ORDER.each { |prop|
|
23
|
+
if prop.match(/\*$/)
|
24
|
+
prefix = prop.chop
|
25
|
+
package.map.delete_if { |key, val|
|
26
|
+
new_map[key] = val if key.match(/^#{prefix}/)
|
27
|
+
}
|
28
|
+
else
|
29
|
+
new_map[prop] = package.map.delete(prop) if package.map[prop]
|
30
|
+
end
|
31
|
+
}
|
32
|
+
if package.map['metadata'] && package.map['metadata'].empty?
|
33
|
+
package.map.delete('metadata')
|
34
|
+
end
|
35
|
+
package.map.each_pair { |key, val|
|
36
|
+
new_map[key] = val if val
|
37
|
+
}
|
38
|
+
package.map = new_map
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
data/lib/bookbinder.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'zip'
|
4
|
+
require 'json'
|
5
|
+
require 'time'
|
6
|
+
require 'cgi'
|
7
|
+
|
8
|
+
module Bookbinder
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'bookbinder/version'
|
12
|
+
require 'bookbinder/media_type'
|
13
|
+
require 'bookbinder/operations'
|
14
|
+
require 'bookbinder/file_system'
|
15
|
+
require 'bookbinder/package'
|
16
|
+
require 'bookbinder/file'
|
17
|
+
require 'bookbinder/document_proxy'
|
18
|
+
require 'bookbinder/transform'
|
19
|
+
require 'bookbinder/transform/organizer'
|
20
|
+
require 'bookbinder/transform/generator'
|
21
|
+
|
22
|
+
# File system types
|
23
|
+
require 'bookbinder/file_system/memory'
|
24
|
+
require 'bookbinder/file_system/directory'
|
25
|
+
require 'bookbinder/file_system/zip_file'
|
26
|
+
|
27
|
+
# Package types
|
28
|
+
require 'bookbinder/package/epub'
|
29
|
+
require 'bookbinder/package/openbook'
|
metadata
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bookbinder
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Joseph Pearson
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-08-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: nokogiri
|
16
|
+
requirement: &11209620 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *11209620
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rubyzip
|
27
|
+
requirement: &11208800 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - =
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.0.0
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *11208800
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: mime-types
|
38
|
+
requirement: &11208160 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *11208160
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: &11207480 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *11207480
|
58
|
+
description: Convert easily between common ebook formats.
|
59
|
+
email:
|
60
|
+
- jpearson@overdrive.com
|
61
|
+
executables:
|
62
|
+
- bookbinder
|
63
|
+
extensions: []
|
64
|
+
extra_rdoc_files: []
|
65
|
+
files:
|
66
|
+
- bin/bookbinder
|
67
|
+
- lib/bookbinder/document_proxy.rb
|
68
|
+
- lib/bookbinder/file.rb
|
69
|
+
- lib/bookbinder/file_system/directory.rb
|
70
|
+
- lib/bookbinder/file_system/memory.rb
|
71
|
+
- lib/bookbinder/file_system/zip_file.rb
|
72
|
+
- lib/bookbinder/file_system.rb
|
73
|
+
- lib/bookbinder/media_type.rb
|
74
|
+
- lib/bookbinder/operations.rb
|
75
|
+
- lib/bookbinder/package/epub.rb
|
76
|
+
- lib/bookbinder/package/openbook.rb
|
77
|
+
- lib/bookbinder/package.rb
|
78
|
+
- lib/bookbinder/transform/epub/audio_overlay.rb
|
79
|
+
- lib/bookbinder/transform/epub/audio_soundtrack.rb
|
80
|
+
- lib/bookbinder/transform/epub/contributor.rb
|
81
|
+
- lib/bookbinder/transform/epub/cover_image.rb
|
82
|
+
- lib/bookbinder/transform/epub/cover_page.rb
|
83
|
+
- lib/bookbinder/transform/epub/creator.rb
|
84
|
+
- lib/bookbinder/transform/epub/description.rb
|
85
|
+
- lib/bookbinder/transform/epub/language.rb
|
86
|
+
- lib/bookbinder/transform/epub/metadata.rb
|
87
|
+
- lib/bookbinder/transform/epub/nav.rb
|
88
|
+
- lib/bookbinder/transform/epub/nav_toc.rb
|
89
|
+
- lib/bookbinder/transform/epub/ncx.rb
|
90
|
+
- lib/bookbinder/transform/epub/ocf.rb
|
91
|
+
- lib/bookbinder/transform/epub/opf.rb
|
92
|
+
- lib/bookbinder/transform/epub/package_identifier.rb
|
93
|
+
- lib/bookbinder/transform/epub/rendition.rb
|
94
|
+
- lib/bookbinder/transform/epub/resources.rb
|
95
|
+
- lib/bookbinder/transform/epub/spine.rb
|
96
|
+
- lib/bookbinder/transform/epub/title.rb
|
97
|
+
- lib/bookbinder/transform/epub/version.rb
|
98
|
+
- lib/bookbinder/transform/generator.rb
|
99
|
+
- lib/bookbinder/transform/openbook/json.rb
|
100
|
+
- lib/bookbinder/transform/organizer.rb
|
101
|
+
- lib/bookbinder/transform.rb
|
102
|
+
- lib/bookbinder/version.rb
|
103
|
+
- lib/bookbinder.rb
|
104
|
+
- Rakefile
|
105
|
+
- README.md
|
106
|
+
homepage: http://github.com/bksh/bookbinder
|
107
|
+
licenses: []
|
108
|
+
post_install_message:
|
109
|
+
rdoc_options: []
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
none: false
|
120
|
+
requirements:
|
121
|
+
- - ! '>='
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
requirements: []
|
125
|
+
rubyforge_project:
|
126
|
+
rubygems_version: 1.8.11
|
127
|
+
signing_key:
|
128
|
+
specification_version: 3
|
129
|
+
summary: Ebook format conversion.
|
130
|
+
test_files: []
|
131
|
+
has_rdoc:
|