bindery 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/bindery/content_methods.rb +8 -4
- data/lib/bindery/division.rb +5 -5
- data/lib/bindery/formats/epub.rb +41 -39
- data/lib/bindery/version.rb +1 -1
- metadata +3 -3
@@ -19,18 +19,22 @@ module Bindery
|
|
19
19
|
def chapter(title, filename, options={}, &block)
|
20
20
|
div('chapter', title, filename, options, &block)
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
def section(title, filename, options={}, &block)
|
24
24
|
div('section', title, filename, options, &block)
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
def part(title, filename, options={}, &block)
|
28
28
|
div('part', title, filename, options, &block)
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
def appendix(title, filename, options={}, &block)
|
32
32
|
div('appendix', title, filename, options, &block)
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
|
+
def index(title, filename, options={}, &block)
|
36
|
+
div('index', title, filename, options, &block)
|
37
|
+
end
|
38
|
+
|
35
39
|
end
|
36
40
|
end
|
data/lib/bindery/division.rb
CHANGED
@@ -6,14 +6,14 @@ module Bindery
|
|
6
6
|
attr_accessor :options
|
7
7
|
|
8
8
|
include ContentMethods
|
9
|
-
|
9
|
+
|
10
10
|
def initialize(div_type, title, file, options)
|
11
11
|
self.div_type = div_type
|
12
12
|
self.title = title
|
13
13
|
self.file = file
|
14
14
|
self.options = options
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def valid?
|
18
18
|
true
|
19
19
|
# ??? title specified? Is this required by the spec? Think about
|
@@ -25,17 +25,17 @@ module Bindery
|
|
25
25
|
# chapters that would break up the text flow?
|
26
26
|
# file exists, readable
|
27
27
|
# file content properly formed? Does that matter? Can we
|
28
|
-
# verify it?
|
28
|
+
# verify it?
|
29
29
|
end
|
30
30
|
|
31
31
|
def divisions
|
32
32
|
@divisions ||= []
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
def body_only?
|
36
36
|
options.fetch(:body_only, true)
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
def include_images?
|
40
40
|
options.fetch(:include_images, true)
|
41
41
|
end
|
data/lib/bindery/formats/epub.rb
CHANGED
@@ -2,10 +2,11 @@ require 'builder'
|
|
2
2
|
require 'zip'
|
3
3
|
require 'bindery/extensions/zip_file'
|
4
4
|
require 'nokogiri'
|
5
|
+
require 'uri'
|
5
6
|
|
6
7
|
module Bindery
|
7
8
|
module Formats
|
8
|
-
|
9
|
+
|
9
10
|
# Builds an EPUB book file from the book description.
|
10
11
|
#
|
11
12
|
# The {EPUB Wikipedia entry}[http://en.wikipedia.org/wiki/EPUB] provides a nice, concise overview of the EPUB format.
|
@@ -20,25 +21,25 @@ module Bindery
|
|
20
21
|
# * Details of the format of other files allowed in EPUB documents are found in
|
21
22
|
# {Open Publication Structure (OPS) 2.0.1 - Recommended Specification}[http://idpf.org/epub/20/spec/OPS_2.0.1_draft.htm].
|
22
23
|
class Epub
|
23
|
-
|
24
|
+
|
24
25
|
MimeTypes = {
|
25
26
|
'.jpg' => 'image/jpeg',
|
26
27
|
'.png' => 'image/png',
|
27
28
|
'.gif' => 'image/gif',
|
28
29
|
}
|
29
|
-
|
30
|
+
|
30
31
|
class ManifestEntry < Struct.new(:file_name, :xml_id, :mime_type)
|
31
32
|
end
|
32
|
-
|
33
|
+
|
33
34
|
attr_accessor :book, :manifest_entries
|
34
|
-
|
35
|
+
|
35
36
|
def initialize(book)
|
36
37
|
self.book = book
|
37
38
|
book.extend BookMethods
|
38
39
|
book.divisions.each{|division| division.extend DivisionMethods}
|
39
40
|
self.manifest_entries = []
|
40
41
|
end
|
41
|
-
|
42
|
+
|
42
43
|
def generate
|
43
44
|
File.delete(book.epub_output_file) if File.exist?(book.epub_output_file)
|
44
45
|
Zip::ZipFile.open(book.epub_output_file, Zip::ZipFile::CREATE) do |zipfile|
|
@@ -46,12 +47,12 @@ module Bindery
|
|
46
47
|
zipfile.write_uncompressed_file 'mimetype', mimetype
|
47
48
|
zipfile.mkdir 'META-INF'
|
48
49
|
zipfile.write_file 'META-INF/container.xml', container
|
49
|
-
|
50
|
+
|
50
51
|
# also frontmatter, backmatter
|
51
52
|
book.divisions.each do |division|
|
52
53
|
write_division(division, zipfile)
|
53
54
|
end
|
54
|
-
|
55
|
+
|
55
56
|
zipfile.mkdir 'css'
|
56
57
|
zipfile.write_file 'css/book.css', stylesheet
|
57
58
|
|
@@ -59,13 +60,13 @@ module Bindery
|
|
59
60
|
zipfile.write_file 'book.ncx', ncx
|
60
61
|
end
|
61
62
|
end
|
62
|
-
|
63
|
+
|
63
64
|
def mimetype
|
64
65
|
# the mimetype file must be the first file in the archive
|
65
66
|
# it must be ASCII, uncompressed, and unencrypted
|
66
67
|
'application/epub+zip'
|
67
68
|
end
|
68
|
-
|
69
|
+
|
69
70
|
def container
|
70
71
|
%q{|<?xml version="1.0" encoding="UTF-8" ?>
|
71
72
|
|<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
|
@@ -75,23 +76,23 @@ module Bindery
|
|
75
76
|
|</container>
|
76
77
|
|}.strip_margin
|
77
78
|
end
|
78
|
-
|
79
|
+
|
79
80
|
def opf
|
80
81
|
xm = Builder::XmlMarkup.new(:indent => 2)
|
81
82
|
xm.instruct!
|
82
83
|
xm.package('version'=>'2.0', 'xmlns'=>'http://www.idpf.org/2007/opf', 'unique-identifier'=>'BookId') {
|
83
|
-
|
84
|
+
|
84
85
|
xm.metadata('xmlns:dc'=>'http://purl.org/dc/elements/1.1/', 'xmlns:opf'=>'http://www.idpf.org/2007/opf') {
|
85
86
|
# required elements
|
86
87
|
xm.dc :title, book.full_title
|
87
88
|
xm.dc :language, book.language
|
88
89
|
xm.dc :identifier, book.url, ident_options('opf:scheme'=>'URL') if book.url
|
89
|
-
xm.dc :identifier, book.isbn, ident_options('opf:scheme'=>'ISBN') if book.isbn
|
90
|
-
|
90
|
+
xm.dc :identifier, book.isbn, ident_options('opf:scheme'=>'ISBN') if book.isbn
|
91
|
+
|
91
92
|
# optional elements
|
92
93
|
xm.dc :creator, book.author, 'opf:role'=>'aut' if book.author
|
93
94
|
}
|
94
|
-
|
95
|
+
|
95
96
|
xm.manifest {
|
96
97
|
book.divisions.each{|division| division.write_item(xm)}
|
97
98
|
# also frontmatter, backmatter
|
@@ -102,17 +103,17 @@ module Bindery
|
|
102
103
|
# xm.item 'id'=>'myfont', 'href'=>'css/myfont.otf', 'media-type'=>'application/x-font-opentype'
|
103
104
|
xm.item 'id'=>'ncx', 'href'=>'book.ncx', 'media-type'=>'application/x-dtbncx+xml'
|
104
105
|
}
|
105
|
-
|
106
|
+
|
106
107
|
xm.spine('toc'=>'ncx') {
|
107
108
|
book.divisions.each{|division| division.write_itemref(xm)}
|
108
109
|
}
|
109
|
-
|
110
|
+
|
110
111
|
# xm.guide {
|
111
112
|
# xm.reference 'type'='loi', 'title'=>'List of Illustrations', 'href'=>'appendix.html#figures'
|
112
113
|
# }
|
113
114
|
}
|
114
115
|
end
|
115
|
-
|
116
|
+
|
116
117
|
def ncx
|
117
118
|
xm = Builder::XmlMarkup.new(:indent => 2)
|
118
119
|
xm.instruct!
|
@@ -124,18 +125,18 @@ module Bindery
|
|
124
125
|
xm.meta 'name'=>'dtb:totalPageCount', 'content'=>0
|
125
126
|
xm.meta 'name'=>'dtb:maxPageNumber', 'content'=>0
|
126
127
|
}
|
127
|
-
|
128
|
+
|
128
129
|
xm.docTitle {
|
129
130
|
xm.text book.full_title
|
130
131
|
}
|
131
|
-
|
132
|
+
|
132
133
|
xm.docAuthor {
|
133
134
|
xm.text book.author
|
134
135
|
}
|
135
|
-
|
136
|
+
|
136
137
|
xm.navMap {
|
137
138
|
play_order = 0
|
138
|
-
|
139
|
+
|
139
140
|
# also frontmatter, backmatter
|
140
141
|
book.divisions.each do |division|
|
141
142
|
play_order += 1
|
@@ -174,7 +175,7 @@ module Bindery
|
|
174
175
|
write_division(div, zipfile)
|
175
176
|
end
|
176
177
|
end
|
177
|
-
|
178
|
+
|
178
179
|
def include_images(doc, zipfile)
|
179
180
|
# TODO: where else can images appear? Style sheets?
|
180
181
|
zipfile.mkdir('images') unless zip_dir_exists?(zipfile, 'images')
|
@@ -195,12 +196,12 @@ module Bindery
|
|
195
196
|
end
|
196
197
|
end
|
197
198
|
end
|
198
|
-
|
199
|
+
|
199
200
|
def add_manifest_entry(file_name)
|
200
201
|
xml_id, ext = File.base_parts(file_name.gsub('/', '-'))
|
201
202
|
manifest_entries << ManifestEntry.new(file_name, xml_id, MimeTypes[ext])
|
202
203
|
end
|
203
|
-
|
204
|
+
|
204
205
|
def cover
|
205
206
|
xm = Builder::XmlMarkup.new(:indent => 2)
|
206
207
|
xm.instruct!
|
@@ -223,7 +224,7 @@ module Bindery
|
|
223
224
|
}
|
224
225
|
}
|
225
226
|
end
|
226
|
-
|
227
|
+
|
227
228
|
def stylesheet
|
228
229
|
# This is a start, but needs work.
|
229
230
|
%q{|@page {
|
@@ -257,7 +258,7 @@ module Bindery
|
|
257
258
|
|h3.section_title {text-align: center;}
|
258
259
|
|}.strip_margin
|
259
260
|
end
|
260
|
-
|
261
|
+
|
261
262
|
def ident_options(opts)
|
262
263
|
if book.isbn
|
263
264
|
return opts.merge('id'=>'BookId') if opts['opf:scheme'] == 'ISBN'
|
@@ -266,18 +267,19 @@ module Bindery
|
|
266
267
|
end
|
267
268
|
opts
|
268
269
|
end
|
269
|
-
|
270
|
+
|
270
271
|
def zip_dir_exists?(zipfile, dirname)
|
271
272
|
dirname = "#{dirname}/" unless dirname =~ %r{/$}
|
272
273
|
zipfile.entries.any?{|e| e.directory? && e.name == dirname}
|
273
274
|
end
|
274
|
-
|
275
|
+
|
275
276
|
def zip_file_exists?(zipfile, filename)
|
276
277
|
zipfile.entries.any?{|e| e.name == filename}
|
277
278
|
end
|
278
|
-
|
279
|
+
|
279
280
|
def make_image_file_name(zipfile, url)
|
280
|
-
|
281
|
+
uri = URI(url)
|
282
|
+
stem, ext = File.base_parts(uri.path)
|
281
283
|
filename = "images/#{stem}#{ext}"
|
282
284
|
n = 0
|
283
285
|
while zip_file_exists?(zipfile, filename)
|
@@ -286,31 +288,31 @@ module Bindery
|
|
286
288
|
end
|
287
289
|
filename
|
288
290
|
end
|
289
|
-
|
291
|
+
|
290
292
|
module BookMethods
|
291
293
|
def epub_output_file
|
292
294
|
@epub_output_file ||= "#{output}.epub"
|
293
295
|
end
|
294
|
-
|
296
|
+
|
295
297
|
def depth
|
296
298
|
(divisions.map(&:depth) + [0]).max
|
297
299
|
end
|
298
|
-
|
300
|
+
|
299
301
|
def ident
|
300
302
|
isbn || url
|
301
303
|
end
|
302
304
|
end
|
303
|
-
|
305
|
+
|
304
306
|
module DivisionMethods
|
305
307
|
|
306
308
|
def self.extended(obj)
|
307
309
|
obj.divisions.each{|division| division.extend DivisionMethods}
|
308
310
|
end
|
309
|
-
|
311
|
+
|
310
312
|
def epub_id
|
311
313
|
@epub_id ||= File.stemname(file)
|
312
314
|
end
|
313
|
-
|
315
|
+
|
314
316
|
def epub_output_file
|
315
317
|
@epub_output_file ||= "#{epub_id}.xhtml"
|
316
318
|
end
|
@@ -346,14 +348,14 @@ module Bindery
|
|
346
348
|
end
|
347
349
|
|
348
350
|
end
|
349
|
-
|
351
|
+
|
350
352
|
module MetadataMethods
|
351
353
|
def to_xml(builder)
|
352
354
|
builder.meta(options.merge(:name => name, :content => value))
|
353
355
|
%{<dc:#{name}>#{value}</dc:#{name}>}
|
354
356
|
end
|
355
357
|
end
|
356
|
-
|
358
|
+
|
357
359
|
module DublinMetadataMethods
|
358
360
|
def to_xml(builder)
|
359
361
|
builder.dc name, value, options
|
data/lib/bindery/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bindery
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.1.0
|
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:
|
12
|
+
date: 2016-06-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: builder
|
@@ -173,7 +173,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
173
173
|
version: '0'
|
174
174
|
segments:
|
175
175
|
- 0
|
176
|
-
hash:
|
176
|
+
hash: 4117656268896811284
|
177
177
|
requirements: []
|
178
178
|
rubyforge_project: bindery
|
179
179
|
rubygems_version: 1.8.23
|