bookbinder 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/README.md +97 -0
  2. data/Rakefile +12 -0
  3. data/bin/bookbinder +17 -0
  4. data/lib/bookbinder/document_proxy.rb +171 -0
  5. data/lib/bookbinder/file.rb +149 -0
  6. data/lib/bookbinder/file_system/directory.rb +62 -0
  7. data/lib/bookbinder/file_system/memory.rb +57 -0
  8. data/lib/bookbinder/file_system/zip_file.rb +106 -0
  9. data/lib/bookbinder/file_system.rb +35 -0
  10. data/lib/bookbinder/media_type.rb +17 -0
  11. data/lib/bookbinder/operations.rb +59 -0
  12. data/lib/bookbinder/package/epub.rb +69 -0
  13. data/lib/bookbinder/package/openbook.rb +33 -0
  14. data/lib/bookbinder/package.rb +295 -0
  15. data/lib/bookbinder/transform/epub/audio_overlay.rb +227 -0
  16. data/lib/bookbinder/transform/epub/audio_soundtrack.rb +73 -0
  17. data/lib/bookbinder/transform/epub/contributor.rb +11 -0
  18. data/lib/bookbinder/transform/epub/cover_image.rb +80 -0
  19. data/lib/bookbinder/transform/epub/cover_page.rb +148 -0
  20. data/lib/bookbinder/transform/epub/creator.rb +67 -0
  21. data/lib/bookbinder/transform/epub/description.rb +43 -0
  22. data/lib/bookbinder/transform/epub/language.rb +29 -0
  23. data/lib/bookbinder/transform/epub/metadata.rb +140 -0
  24. data/lib/bookbinder/transform/epub/nav.rb +60 -0
  25. data/lib/bookbinder/transform/epub/nav_toc.rb +177 -0
  26. data/lib/bookbinder/transform/epub/ncx.rb +63 -0
  27. data/lib/bookbinder/transform/epub/ocf.rb +33 -0
  28. data/lib/bookbinder/transform/epub/opf.rb +22 -0
  29. data/lib/bookbinder/transform/epub/package_identifier.rb +87 -0
  30. data/lib/bookbinder/transform/epub/rendition.rb +265 -0
  31. data/lib/bookbinder/transform/epub/resources.rb +38 -0
  32. data/lib/bookbinder/transform/epub/spine.rb +79 -0
  33. data/lib/bookbinder/transform/epub/title.rb +92 -0
  34. data/lib/bookbinder/transform/epub/version.rb +39 -0
  35. data/lib/bookbinder/transform/generator.rb +8 -0
  36. data/lib/bookbinder/transform/openbook/json.rb +15 -0
  37. data/lib/bookbinder/transform/organizer.rb +41 -0
  38. data/lib/bookbinder/transform.rb +7 -0
  39. data/lib/bookbinder/version.rb +5 -0
  40. data/lib/bookbinder.rb +29 -0
  41. 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,8 @@
1
+ class Bookbinder::Transform::Generator < Bookbinder::Transform
2
+
3
+ def to_map(package)
4
+ package.map['generator'] = "Bookbinder #{Bookbinder::VERSION}"
5
+ end
6
+
7
+ end
8
+
@@ -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
@@ -0,0 +1,7 @@
1
+ class Bookbinder::Transform
2
+
3
+ def dependencies
4
+ []
5
+ end
6
+
7
+ end
@@ -0,0 +1,5 @@
1
+ module Bookbinder
2
+
3
+ VERSION = "0.2.0"
4
+
5
+ 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: