gepub 0.6.8.5 → 0.6.8.6

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # gepub [<img src="https://secure.travis-ci.org/skoji/gepub.png" />](http://travis-ci.org/skoji/gepub) [<img src="https://gemnasium.com/skoji/gepub.png" />](https://gemnasium.com/skoji/gepub)
1
+ # gepub [<img src="https://secure.travis-ci.org/skoji/gepub.png" />](http://travis-ci.org/skoji/gepub)
2
2
 
3
3
  * http://en.skoji.jp/category/gepub/
4
4
 
data/gepub.gemspec CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
18
18
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
19
  s.require_paths = ["lib"]
20
20
 
21
- s.add_development_dependency "rspec", "~> 2"
21
+ s.add_development_dependency "rspec", "~> 2.11"
22
22
  s.add_runtime_dependency "nokogiri", "~> 1.5.0"
23
23
  s.add_runtime_dependency "rubyzip", "~> 0.9.7"
24
24
  end
data/lib/gepub/book.rb CHANGED
@@ -104,41 +104,9 @@ module GEPUB
104
104
  package_path = nil
105
105
  Zip::ZipInputStream::open_buffer(io) {
106
106
  |zis|
107
- while entry = zis.get_next_entry
108
- if !entry.directory?
109
- files[entry.name] = zis.read
110
- case entry.name
111
- when MIMETYPE then
112
- if files[MIMETYPE] != MIMETYPE_CONTENTS
113
- warn "#{MIMETYPE} is not valid: should be #{MIMETYPE_CONTENTS} but was #{files[MIMETYPE]}"
114
- end
115
- files.delete(MIMETYPE)
116
- when CONTAINER then
117
- package_path = rootfile_from_container(files[CONTAINER])
118
- files.delete(CONTAINER)
119
- when ROOTFILE_PATTERN then
120
- package = Package.parse_opf(files[entry.name], entry.name)
121
- files.delete(entry.name)
122
- end
123
- end
124
- end
125
-
126
- if package.nil?
127
- raise 'this container do not cotains publication information file'
128
- end
129
-
130
- if package_path != package.path
131
- warn 'inconsistend EPUB file: container says opf is #{package_path}, but actually #{package.path}'
132
- end
133
-
134
- files.each {
135
- |k, content|
136
- item = package.manifest.item_by_href(k.sub(/^#{package.contents_prefix}/,''))
137
- if !item.nil?
138
- files.delete(k)
139
- item.add_raw_content(content)
140
- end
141
- }
107
+ package, package_path = parse_container(zis, files)
108
+ check_consistency_of_package(package, package_path)
109
+ parse_files_into_package(files, package)
142
110
  book = Book.new(package.path)
143
111
  book.instance_eval { @package = package; @optional_files = files }
144
112
  book
@@ -226,8 +194,8 @@ module GEPUB
226
194
  items[@package.bindings.handler_by_media_type[media_type]]
227
195
  end
228
196
 
229
- def method_missing(name,*args)
230
- @package.send(name, *args)
197
+ def method_missing(name,*args, &block)
198
+ @package.send(name, *args, &block)
231
199
  end
232
200
 
233
201
  # should call ordered() with block.
@@ -239,34 +207,8 @@ module GEPUB
239
207
  # clenup and maintain consistency of metadata and items included in the Book
240
208
  # object.
241
209
  def cleanup
242
- if version.to_f < 3.0 || @package.epub_backward_compat
243
- if @package.manifest.item_list.select {
244
- |x,item|
245
- item.media_type == 'application/x-dtbncx+xml'
246
- }.size == 0
247
- if (@toc.size == 0)
248
- @toc << { :item => @package.manifest.item_list[@package.spine.itemref_list[0].idref] }
249
- end
250
- add_item('toc.ncx', StringIO.new(ncx_xml), 'ncx')
251
- end
252
- end
253
-
254
- if version.to_f >=3.0
255
- @package.metadata.set_lastmodified
256
-
257
- if @package.manifest.item_list.select {
258
- |href, item|
259
- (item.properties||[]).member? 'nav'
260
- }.size == 0
261
- generate_nav_doc
262
- end
263
-
264
- @package.spine.remove_with_idlist @package.manifest.item_list.map {
265
- |href, item|
266
- item.fallback
267
- }.reject(&:nil?)
268
-
269
- end
210
+ cleanup_for_epub2
211
+ cleanup_for_epub3
270
212
  end
271
213
 
272
214
  # write EPUB to stream specified by the argument.
@@ -388,5 +330,80 @@ EOF
388
330
  }
389
331
  builder.to_xml(:encoding => 'utf-8')
390
332
  end
333
+
334
+ private
335
+ def self.parse_container(zis, files)
336
+ package_path = nil
337
+ package = nil
338
+ while entry = zis.get_next_entry
339
+ if !entry.directory?
340
+ files[entry.name] = zis.read
341
+ case entry.name
342
+ when MIMETYPE then
343
+ if files[MIMETYPE] != MIMETYPE_CONTENTS
344
+ warn "#{MIMETYPE} is not valid: should be #{MIMETYPE_CONTENTS} but was #{files[MIMETYPE]}"
345
+ end
346
+ files.delete(MIMETYPE)
347
+ when CONTAINER then
348
+ package_path = rootfile_from_container(files[CONTAINER])
349
+ files.delete(CONTAINER)
350
+ when ROOTFILE_PATTERN then
351
+ package = Package.parse_opf(files[entry.name], entry.name)
352
+ files.delete(entry.name)
353
+ end
354
+ end
355
+ end
356
+ return package, package_path
357
+ end
358
+
359
+ def self.check_consistency_of_package(package, package_path)
360
+ if package.nil?
361
+ raise 'this container do not cotains publication information file'
362
+ end
363
+
364
+ if package_path != package.path
365
+ warn 'inconsistend EPUB file: container says opf is #{package_path}, but actually #{package.path}'
366
+ end
367
+ end
368
+ def self.parse_files_into_package(files, package)
369
+ files.each {
370
+ |k, content|
371
+ item = package.manifest.item_by_href(k.sub(/^#{package.contents_prefix}/,''))
372
+ if !item.nil?
373
+ files.delete(k)
374
+ item.add_raw_content(content)
375
+ end
376
+ }
377
+ end
378
+ def cleanup_for_epub2
379
+ if version.to_f < 3.0 || @package.epub_backward_compat
380
+ if @package.manifest.item_list.select {
381
+ |x,item|
382
+ item.media_type == 'application/x-dtbncx+xml'
383
+ }.size == 0
384
+ if (@toc.size == 0)
385
+ @toc << { :item => @package.manifest.item_list[@package.spine.itemref_list[0].idref] }
386
+ end
387
+ add_item('toc.ncx', StringIO.new(ncx_xml), 'ncx')
388
+ end
389
+ end
390
+ end
391
+ def cleanup_for_epub3
392
+ if version.to_f >=3.0
393
+ @package.metadata.set_lastmodified
394
+
395
+ if @package.manifest.item_list.select {
396
+ |href, item|
397
+ (item.properties||[]).member? 'nav'
398
+ }.size == 0
399
+ generate_nav_doc
400
+ end
401
+
402
+ @package.spine.remove_with_idlist @package.manifest.item_list.map {
403
+ |href, item|
404
+ item.fallback
405
+ }.reject(&:nil?)
406
+ end
407
+ end
391
408
  end
392
409
  end
@@ -1,6 +1,6 @@
1
1
  module GEPUB
2
2
  module BuilderMixin
3
- def method_missing(name, *args)
3
+ def method_missing(name, *args, &block)
4
4
  if Array === @last_defined_item &&
5
5
  @last_defined_item.size > 0 &&
6
6
  @last_defined_item[0].respond_to?(name.to_sym)
@@ -20,7 +20,7 @@ module GEPUB
20
20
  end
21
21
  }
22
22
  elsif @last_defined_item.respond_to?(name.to_sym)
23
- @last_defined_item.send(name, *args)
23
+ @last_defined_item.send(name, *args, &block)
24
24
  else
25
25
  super
26
26
  end
data/lib/gepub/meta.rb CHANGED
@@ -48,7 +48,7 @@ module GEPUB
48
48
 
49
49
  # add a refiner.
50
50
  def add_refiner(property, content, attributes = {})
51
- (@refiners[property] ||= []) << refiner = Meta.new('meta', content, @parent, { 'property' => property }.merge(attributes)) unless content.nil?
51
+ (@refiners[property] ||= []) << Meta.new('meta', content, @parent, { 'property' => property }.merge(attributes)) unless content.nil?
52
52
  self
53
53
  end
54
54
 
@@ -69,6 +69,14 @@ module GEPUB
69
69
  define_method(methodbase) { refiner(name) }
70
70
  }
71
71
 
72
+ def lang=(lang)
73
+ @attributes['xml:lang'] = lang
74
+ end
75
+
76
+ def lang
77
+ @attributes['xml:lang']
78
+ end
79
+
72
80
  # add alternate script refiner.
73
81
  def add_alternates(alternates = {})
74
82
  alternates.each {
@@ -0,0 +1,10 @@
1
+ class Array
2
+ def sort_as_meta
3
+ i = 0
4
+ sort_by {
5
+ |v|
6
+ seq = v.display_seq
7
+ [ seq && seq.to_s.to_i || 2 ** (0.size * 8 - 2) - 1, i += 1]
8
+ }
9
+ end
10
+ end
@@ -12,6 +12,11 @@ module GEPUB
12
12
  end
13
13
  # Holds data in /package/metadata
14
14
  class Metadata
15
+ class NilContent
16
+ def self.content
17
+ nil
18
+ end
19
+ end
15
20
  include XMLUtil
16
21
  attr_accessor :opf_version
17
22
  # parse metadata element. metadata_xml should be Nokogiri::XML::Node object.
@@ -23,18 +28,26 @@ module GEPUB
23
28
  @namespaces = @xml.namespaces
24
29
  CONTENT_NODE_LIST.each {
25
30
  |node|
26
- i = 0
27
- @content_nodes[node] = parse_node(DC_NS, node).sort_by {
28
- |v|
29
- [v.refiner('display-seq').to_s.to_i || 2 ** (0.size * 8 - 2) - 1, i += 1]
30
- }
31
+ @content_nodes[node] = parse_node(DC_NS, node).sort_as_meta
31
32
  }
32
33
  @xml.xpath("#{ns_prefix(OPF_NS)}:meta[not(@refines) and @property]", @namespaces).each {
33
34
  |node|
34
- @meta[node['property']] = create_meta(node)
35
+ (@content_nodes['meta'] ||= []) << create_meta(node)
35
36
  }
36
37
 
37
38
  @oldstyle_meta = parse_opf2_meta
39
+
40
+ meta_list.each {
41
+ |metanode|
42
+ case metanode['property']
43
+ when 'rendition:layout'
44
+ @layout = metanode
45
+ when 'rendition:orientation'
46
+ @orientation = metanode
47
+ when 'rendition:spread'
48
+ @spread = metanode
49
+ end
50
+ }
38
51
  }
39
52
  }
40
53
  end
@@ -43,15 +56,20 @@ module GEPUB
43
56
  @id_pool = id_pool
44
57
  @metalist = {}
45
58
  @content_nodes = {}
46
- @meta = {}
47
59
  @oldstyle_meta = []
48
60
  @opf_version = opf_version
49
61
  @namespaces = { 'xmlns:dc' => DC_NS }
50
62
  @namespaces['xmlns:opf'] = OPF_NS if @opf_version.to_f < 3.0
63
+ @default_layout = 'reflowable'
64
+ @default_orientation = 'auto'
65
+ @spread = 'auto'
66
+ @layout = NilContent
67
+ @orientation = NilContent
68
+ @spread = NilContent
51
69
  yield self if block_given?
52
70
  end
53
71
 
54
- def to_xml(builder)
72
+ def to_xml(builder)
55
73
  builder.metadata(@namespaces) {
56
74
  @content_nodes.each {
57
75
  |name, list|
@@ -86,7 +104,7 @@ module GEPUB
86
104
 
87
105
  CONTENT_NODE_LIST = ['identifier','title', 'language', 'contributor', 'creator', 'coverage', 'date','description','format','publisher','relation','rights','source','subject','type'].each {
88
106
  |node|
89
- define_method(node + '_list') { @content_nodes[node].dup }
107
+ define_method(node + '_list') { @content_nodes[node].dup.sort_as_meta }
90
108
  define_method(node + '_clear') {
91
109
  if !@content_nodes[node].nil?
92
110
  @content_nodes[node].each { |x| unregister_meta(x) };
@@ -115,6 +133,17 @@ module GEPUB
115
133
  }
116
134
  }
117
135
 
136
+ def meta_list
137
+ (@content_nodes['meta'] || []).sort_as_meta.dup
138
+ end
139
+
140
+ def meta_clear
141
+ if !@content_nodes['meta'].nil?
142
+ @content_nodes['meta'].each { |x| unregister_meta(x) };
143
+ @content_nodes['meta'] = []
144
+ end
145
+ end
146
+
118
147
  def title
119
148
  if !@content_nodes['title'].nil?
120
149
  @content_nodes['title'].each do
@@ -127,12 +156,13 @@ module GEPUB
127
156
 
128
157
  def get_first_node(node)
129
158
  if !@content_nodes[node].nil? && @content_nodes[node].size > 0
130
- @content_nodes[node][0]
159
+ @content_nodes[node].sort_as_meta[0]
131
160
  end
132
161
  end
133
162
 
134
- def add_identifier(string, id, type=nil)
135
- raise 'id #{id} is already in use' if @id_pool[id]
163
+ def add_identifier(string, id=nil, type=nil)
164
+ id = @id_pool.generate_key(:prefix => 'BookId') if id.nil?
165
+ raise "id #{id} is already in use" if @id_pool[id]
136
166
  identifier = add_metadata('identifier', string, id)
137
167
  identifier.refine('identifier-type', type) unless type.nil?
138
168
  identifier
@@ -147,12 +177,13 @@ module GEPUB
147
177
  |x|
148
178
  return x.content if x['id'] == id
149
179
  }
180
+ return nil
150
181
  end
151
182
 
152
183
  def add_metadata(name, content, id = nil, itemclass = Meta)
153
184
  meta = itemclass.new(name, content, self, { 'id' => id })
154
185
  (@content_nodes[name] ||= []) << meta
155
- yield self if block_given?
186
+ yield meta if block_given?
156
187
  meta
157
188
  end
158
189
 
@@ -227,15 +258,42 @@ module GEPUB
227
258
  @id_pool[meta['id']] = nil
228
259
  end
229
260
  end
261
+
262
+ def rendition_layout
263
+ @layout.content || @default_layout
264
+ end
265
+
266
+ def rendition_layout=(val)
267
+ @layout = Meta.new('meta', val, self, { 'property' => 'rendition:layout' })
268
+ (@content_nodes['meta'] ||= []) << @layout
269
+ end
270
+
271
+ def rendition_orientation
272
+ @orientation.content || @default_orientation
273
+ end
274
+
275
+ def rendition_orientation=(val)
276
+ @orientation = Meta.new('meta', val, self, { 'property' => 'rendition:orientation' })
277
+ (@content_nodes['meta'] ||= []) << @orientation
278
+ end
279
+
280
+ def rendition_spread
281
+ @spread.content || @default_spread
282
+ end
283
+
284
+ def rendition_spread=(val)
285
+ @spread = Meta.new('meta', val, self, { 'property' => 'rendition:spread' })
286
+ (@content_nodes['meta'] ||= []) << @spread
287
+ end
230
288
 
231
289
  private
232
290
  def parse_node(ns, node)
233
291
  @xml.xpath("#{ns_prefix(ns)}:#{node}", @namespaces).map {
234
- |node|
235
- create_meta(node)
292
+ |n|
293
+ create_meta(n)
236
294
  }
237
295
  end
238
-
296
+
239
297
  def create_meta(node)
240
298
  Meta.new(node.name, node.content, self, attr_to_hash(node.attributes), collect_refiners(node['id']))
241
299
  end
data/lib/gepub/package.rb CHANGED
@@ -7,12 +7,21 @@ module GEPUB
7
7
  class Package
8
8
  include XMLUtil
9
9
  extend Forwardable
10
- attr_accessor :path, :metadata, :manifest, :spine, :bindings, :epub_backward_compat, :contents_prefix
10
+ attr_accessor :path, :metadata, :manifest, :spine, :bindings, :epub_backward_compat, :contents_prefix, :prefixes
11
11
  def_delegators :@manifest, :item_by_href
12
12
  def_delegators :@metadata, *Metadata::CONTENT_NODE_LIST.map {
13
13
  |x|
14
14
  ["#{x}", "#{x}_list", "set_#{x}", "#{x}=", "add_#{x}"]
15
15
  }.flatten
16
+ def_delegators :@metadata, :set_lastmodified
17
+ def_delegators :@metadata, :lastmodified
18
+ def_delegators :@metadata, :rendition_layout
19
+ def_delegators :@metadata, :rendition_layout=
20
+ def_delegators :@metadata, :rendition_orientation
21
+ def_delegators :@metadata, :rendition_orientation=
22
+ def_delegators :@metadata, :rendition_spread
23
+ def_delegators :@metadata, :rendition_spread=
24
+
16
25
  def_delegators :@spine, :page_progression_direction=
17
26
  def_delegators :@spine, :page_progression_direction
18
27
 
@@ -59,6 +68,12 @@ module GEPUB
59
68
  @pool[k] = v
60
69
  end
61
70
  end
71
+
72
+ def parse_prefixes(prefix)
73
+ return {} if prefix.nil?
74
+ m = prefix.scan(/([\S]+): +(\S+)[\s]*/)
75
+ Hash[*m.flatten]
76
+ end
62
77
 
63
78
  # parse OPF data. opf should be io or string object.
64
79
  def self.parse_opf(opf, path)
@@ -73,6 +88,7 @@ module GEPUB
73
88
  @manifest = Manifest.parse(@xml.at_xpath("//#{ns_prefix(OPF_NS)}:manifest"), @attributes['version'], @id_pool)
74
89
  @spine = Spine.parse(@xml.at_xpath("//#{ns_prefix(OPF_NS)}:spine"), @attributes['version'], @id_pool)
75
90
  @bindings = Bindings.parse(@xml.at_xpath("//#{ns_prefix(OPF_NS)}:bindings"))
91
+ @prefixes = parse_prefixes(@attributes['prefix'])
76
92
  }
77
93
  }
78
94
  end
@@ -86,6 +102,7 @@ module GEPUB
86
102
  end
87
103
  @contents_prefix = File.dirname(@path).sub(/^\.$/,'')
88
104
  @contents_prefix = @contents_prefix + '/' if @contents_prefix.size > 0
105
+ @prefixes = {}
89
106
  @namespaces = {'xmlns' => OPF_NS }
90
107
  @attributes = attributes
91
108
  @attributes['version'] ||= '3.0'
@@ -120,10 +137,15 @@ module GEPUB
120
137
  end
121
138
 
122
139
  def identifier=(identifier)
123
- set_main_id(identifier, nil, 'URL')
140
+ set_primary_identifier(identifier, nil, nil)
124
141
  end
125
142
 
126
143
  def set_main_id(identifier, id = nil, type = nil)
144
+ warn 'set_main_id is deprecated. use set_primary_identifier instead.'
145
+ set_primary_identifier(identifier, id, type)
146
+ end
147
+
148
+ def set_primary_identifier(identifier, id = nil, type = nil)
127
149
  set_unique_identifier(id || @id_pool.generate_key(:prefix => 'BookId', :without_count => true))
128
150
  @metadata.add_identifier identifier, unique_identifier, type
129
151
  end
@@ -187,6 +209,7 @@ module GEPUB
187
209
  @metadata.language
188
210
  end
189
211
 
212
+ #TODO maybe it should be 'epub_version'
190
213
  def version
191
214
  @attributes['version']
192
215
  end
@@ -212,6 +235,14 @@ module GEPUB
212
235
  version
213
236
  end
214
237
 
238
+ def enable_rendition
239
+ @prefixes['rendition'] = 'http://www.idpf.org/vocab/rendition/#'
240
+ end
241
+
242
+ def rendition_enabled?
243
+ @prefixes['rendition'] == 'http://www.idpf.org/vocab/rendition/#'
244
+ end
245
+
215
246
  def opf_xml
216
247
  if version.to_f < 3.0 || @epub_backward_compat
217
248
  spine.toc ||= 'ncx'
@@ -230,6 +261,12 @@ module GEPUB
230
261
  end
231
262
  builder = Nokogiri::XML::Builder.new {
232
263
  |xml|
264
+ if @prefixes.size == 0
265
+ @attributes.delete 'prefix'
266
+ else
267
+ @attributes['prefix'] = @prefixes.map { |k, v| "#{k}: #{v}" }.join(' ')
268
+ end
269
+
233
270
  xml.package(@namespaces.merge(@attributes)) {
234
271
  @metadata.to_xml(xml)
235
272
  @manifest.to_xml(xml)
@@ -0,0 +1,49 @@
1
+ module GEPUB
2
+ class Rendition
3
+ class NilContent
4
+ def self.content
5
+ nil
6
+ end
7
+ end
8
+
9
+ def initialize()
10
+ @default_layout = 'reflowable'
11
+ @default_orientation = 'auto'
12
+ @default_spread = 'auto'
13
+ @layout = NilContent
14
+ @orientation = NilContent
15
+ @spread = NilContent
16
+ end
17
+
18
+ def set_metadata(metadata)
19
+ @metadata = metadata
20
+ @metadata.meta_list.each {
21
+ |metanode|
22
+ case metanode['property']
23
+ when 'rendition:layout'
24
+ @layout = metanode
25
+ when 'rendition:orientation'
26
+ @orientation = metanode
27
+ when 'rendition:spread'
28
+ @spread = metanode
29
+ end
30
+ }
31
+ end
32
+
33
+ def layout
34
+ @layout.content || @default_layout
35
+ end
36
+
37
+ def orientation
38
+ @orientation.content || @default_orientation
39
+ end
40
+
41
+ def spread
42
+ @spread.content || @default_spread
43
+ end
44
+
45
+ def value_map
46
+ { 'layout' => layout, 'orientation' => orientation, 'spread' => spread }
47
+ end
48
+ end
49
+ end
@@ -15,8 +15,8 @@ module GEPUB
15
15
  @item.set_media_type(val)
16
16
  end
17
17
 
18
- def method_missing(name, *args)
19
- @item.send(name.to_sym, *args)
18
+ def method_missing(name, *args, &block)
19
+ @item.send(name.to_sym, *args, &block)
20
20
  end
21
21
  end
22
22
 
@@ -83,6 +83,22 @@ module GEPUB
83
83
  itemref.page_spread_right
84
84
  end
85
85
 
86
+ def rendition_layout val
87
+ itemref = @book.spine.itemref_by_id[@last_defined_item.item.id]
88
+ raise 'rendition should be called inside ordered' if (itemref.nil?)
89
+ itemref.rendition_layout = val
90
+ end
91
+ def rendition_orientation val
92
+ itemref = @book.spine.itemref_by_id[@last_defined_item.item.id]
93
+ raise 'rendition should be called inside ordered' if (itemref.nil?)
94
+ itemref.rendition_orientation = val
95
+ end
96
+ def rendition_spread val
97
+ itemref = @book.spine.itemref_by_id[@last_defined_item.item.id]
98
+ raise 'rendition should be called inside ordered' if (itemref.nil?)
99
+ itemref.rendition_spread = val
100
+ end
101
+
86
102
  def linear val
87
103
  itemref = @book.spine.itemref_by_id[@last_defined_item.item.id]
88
104
  raise 'linear should be called inside ordered' if (itemref.nil?)
@@ -105,6 +121,13 @@ module GEPUB
105
121
  def add_resource_dir(name)
106
122
  import "#{name}/resources.conf", :dir_prefix => name
107
123
  end
124
+
125
+ def add_resource_dirs(dirs)
126
+ dirs.each do
127
+ |dir|
128
+ add_resource_dir dir
129
+ end
130
+ end
108
131
 
109
132
  def cover_image(val)
110
133
  file(val)
@@ -0,0 +1,20 @@
1
+ class SortedArray < Array
2
+ def initialize(*args, &sort_by)
3
+ @sort_by = sort_by || Proc.new { |x,y| x <=> y }
4
+ super(*args)
5
+ self.sort!() &sort_by
6
+ end
7
+
8
+ def insert(i, v)
9
+ insert_before = index(find { |x| @sort_by.call(x, v) == 1 })
10
+ super(insert_before ? insert_before : -1, v)
11
+ end
12
+
13
+ def <<(v)
14
+ insert(0, v)
15
+ end
16
+
17
+ alias push <<
18
+ alias unshift <<
19
+
20
+ end
data/lib/gepub/spine.rb CHANGED
@@ -46,6 +46,22 @@ module GEPUB
46
46
  add_property 'page-spread-left'
47
47
  end
48
48
 
49
+ def set_rendition_param(name, val)
50
+ add_property "rendition:#{name}-#{val}"
51
+ end
52
+
53
+ def rendition_layout=(val)
54
+ set_rendition_param('layout', val)
55
+ end
56
+
57
+ def rendition_orientation=(val)
58
+ set_rendition_param('orientation', val)
59
+ end
60
+
61
+ def rendition_spread=(val)
62
+ set_rendition_param('spread', val)
63
+ end
64
+
49
65
  def to_xml(builder, opf_version)
50
66
  attr = @attributes.dup
51
67
  if opf_version.to_f < 3.0
@@ -68,7 +84,7 @@ module GEPUB
68
84
  @xml = spine_xml
69
85
  @namespaces = @xml.namespaces
70
86
  @attributes = attr_to_hash(@xml.attributes)
71
- @item_refs = []
87
+ @item_refs = []
72
88
  @xml.xpath("//#{ns_prefix(OPF_NS)}:spine/#{ns_prefix(OPF_NS)}:itemref", @namespaces).map {
73
89
  |itemref|
74
90
  i = Itemref.create(self, attr_to_hash(itemref.attributes))
data/lib/gepub/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module GEPUB
2
2
  # GEPUB gem version
3
- VERSION = "0.6.8.5"
3
+ VERSION = "0.6.8.6"
4
4
  end
data/lib/gepub.rb CHANGED
@@ -21,6 +21,7 @@ require 'gepub/version'
21
21
  require 'gepub/xml_util'
22
22
  require 'gepub/meta'
23
23
  require 'gepub/datemeta'
24
+ require 'gepub/meta_array'
24
25
  require 'gepub/metadata'
25
26
  require 'gepub/manifest'
26
27
  require 'gepub/spine'
data/spec/book_spec.rb ADDED
@@ -0,0 +1,292 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+ require 'rubygems'
3
+ require 'nokogiri'
4
+
5
+ describe GEPUB::Book do
6
+ context 'on creating new book' do
7
+ describe 'initialize' do
8
+ context 'without parameter' do
9
+ it 'returns empty book' do
10
+ book = GEPUB::Book.new()
11
+ expect(book.path) .to eq('OEBPS/package.opf')
12
+ expect(book.version).to eq('3.0')
13
+ end
14
+ end
15
+ context 'with path' do
16
+ it 'returns empty book with path' do
17
+ book = GEPUB::Book.new('mypath/foo.opf')
18
+ expect(book.path) .to eq('mypath/foo.opf')
19
+ end
20
+ end
21
+ context 'with path and attributes' do
22
+ it 'returns empty book with path and attributes' do
23
+ book = GEPUB::Book.new('mypath/book.opf', {'version' => '2.1'});
24
+ expect(book.path) .to eq('mypath/book.opf')
25
+ expect(book.version).to eq('2.1')
26
+ end
27
+ end
28
+ end
29
+ describe 'version=' do
30
+ context 'overwrite version' do
31
+ it 'will hold new version' do
32
+ book = GEPUB::Book.new()
33
+ book.version = '2.1'
34
+ expect(book.version).to eq('2.1')
35
+ end
36
+ end
37
+ end
38
+ describe 'identifer=' do
39
+ context 'set identifier' do
40
+ it 'will set unique-identifier and related attributes' do
41
+ book = GEPUB::Book.new()
42
+ book.identifier = 'the-book-identifier'
43
+
44
+ expect(book.identifier).to eq('the-book-identifier')
45
+ expect(book.identifier_list[0]['id']).to eq(book.unique_identifier)
46
+ expect(book.identifier_list[0].refiner('identifier-type')).to be_nil
47
+ end
48
+ end
49
+ end
50
+ describe 'set_primary_identifier=' do
51
+ context 'set identifier with id and type' do
52
+ it 'will set unique-identifier and related attributes' do
53
+ book = GEPUB::Book.new()
54
+ book.set_primary_identifier 'http//example.com/the-identifier', 'MyBookID', 'URL'
55
+
56
+ expect(book.identifier).to eq('http//example.com/the-identifier')
57
+ expect(book.unique_identifier).to eq('MyBookID')
58
+ expect(book.identifier_list[0]['id']).to eq('MyBookID')
59
+ expect(book.identifier_list[0].identifier_type.content).to eq('URL')
60
+ end
61
+ end
62
+ end
63
+ describe 'add_identifier' do
64
+ context 'add new identifier to new book' do
65
+ it 'will set identifier, and not set unique-identifier' do
66
+ book = GEPUB::Book.new()
67
+ book.add_identifier 'newid'
68
+ expect(book.unique_identifier).to be_nil
69
+ expect(book.identifier).to be_nil
70
+ expect(book.identifier_list[0].content).to eq('newid')
71
+ end
72
+ end
73
+ context 'add new identifier to existing book' do
74
+ it 'will set new identifier' do
75
+ book = GEPUB::Book.new()
76
+ book.identifier = 'theIdentifier'
77
+ book.add_identifier 'http://example.jp/additional_identifier', nil, 'URL'
78
+ expect(book.identifier).to eq('theIdentifier')
79
+ expect(book.identifier_list[1].content).to eq('http://example.jp/additional_identifier')
80
+ expect(book.identifier_list[1].identifier_type.content).to eq('URL')
81
+ end
82
+ end
83
+ end
84
+ describe 'add_title' do
85
+ context 'add title' do
86
+ it 'adds new title' do
87
+ book = GEPUB::Book.new()
88
+ book.title = 'the title'
89
+ book.add_title 'new title'
90
+ expect(book.title.to_s).to eq('the title')
91
+ expect(book.title_list[1].to_s).to eq('new title')
92
+ end
93
+ end
94
+ end
95
+ describe 'title =' do
96
+ context 'set first title' do
97
+ it 'will add new title' do
98
+ book = GEPUB::Book.new()
99
+ book.title = 'the title'
100
+ expect(book.title.to_s).to eq('the title')
101
+ end
102
+ end
103
+ context 'clear and set title' do
104
+ it 'will clear title and add new title' do
105
+ book = GEPUB::Book.new()
106
+ book.add_title 'the title'
107
+ book.title = 'new title'
108
+ expect(book.title.to_s).to eq('new title')
109
+ end
110
+ end
111
+ end
112
+ describe 'title' do
113
+ context 'main title is specified' do
114
+ it 'returns main title' do
115
+ book = GEPUB::Book.new()
116
+ book.add_title 'sub title'
117
+ book.add_title('the main title', nil, GEPUB::TITLE_TYPE::MAIN)
118
+ expect(book.title.to_s).to eq('the main title')
119
+ end
120
+ end
121
+ context 'display_seq is specified' do
122
+ it 'returns first title' do
123
+ book = GEPUB::Book.new()
124
+ book.add_title 'second title'
125
+ book.add_title('first title') do
126
+ |title|
127
+ title.display_seq = 1
128
+ end
129
+ expect(book.title.to_s).to eq('first title')
130
+ end
131
+ end
132
+ end
133
+ describe 'title_list' do
134
+ context 'main title is specified' do
135
+ it 'returns titles in defined order' do
136
+ book = GEPUB::Book.new()
137
+ book.add_title 'sub title'
138
+ book.add_title('the main title', nil, GEPUB::TITLE_TYPE::MAIN)
139
+ expect(book.title_list[0].to_s).to eq('sub title')
140
+ expect(book.title_list[1].to_s).to eq('the main title')
141
+ end
142
+ context 'display seq is specified' do
143
+ it 'returns titles in display-seq order' do
144
+ book = GEPUB::Book.new()
145
+ book.add_title 'third title'
146
+ book.add_title 'fourth title'
147
+ book.add_title 'second title' do
148
+ |title|
149
+ title.display_seq = 2
150
+ end
151
+ book.add_title('first title') do
152
+ |title|
153
+ title.display_seq = 1
154
+ end
155
+ expect(book.title_list[0].to_s).to eq('first title')
156
+ expect(book.title_list[1].to_s).to eq('second title')
157
+ expect(book.title_list[2].to_s).to eq('third title')
158
+ expect(book.title_list[3].to_s).to eq('fourth title')
159
+ end
160
+ end
161
+ end
162
+ end
163
+ describe 'add_creator' do
164
+ it 'adds new creator' do
165
+ book = GEPUB::Book.new()
166
+ book.creator = 'the creator'
167
+ book.add_creator 'new creator'
168
+ expect(book.creator.to_s).to eq('the creator')
169
+ expect(book.creator_list[1].to_s).to eq('new creator')
170
+ end
171
+ end
172
+ describe 'creator=' do
173
+ it 'set first creator' do
174
+ book = GEPUB::Book.new()
175
+ book.creator = 'the creator'
176
+ expect(book.creator.to_s).to eq('the creator')
177
+ end
178
+ it 'clear and set creator' do
179
+ book = GEPUB::Book.new()
180
+ book.creator = 'the creator'
181
+ book.creator = 'new creator'
182
+ expect(book.creator.to_s).to eq('new creator')
183
+ expect(book.creator_list.size).to eq(1)
184
+ end
185
+ end
186
+ describe 'creator' do
187
+ context 'display seq is specified' do
188
+ it 'shows creator with smallest display-seq' do
189
+ book = GEPUB::Book.new()
190
+ book.add_creator 'a creator'
191
+ book.add_creator('second creator') do
192
+ |creator|
193
+ creator.display_seq = 2
194
+ end
195
+ expect(book.creator.to_s).to eq('second creator')
196
+ end
197
+ end
198
+ end
199
+
200
+ describe 'creator_list' do
201
+ context 'display seq is specified' do
202
+ it 'returns creators in display-seq order' do
203
+ book = GEPUB::Book.new()
204
+ book.add_creator 'a creator'
205
+ book.add_creator('second creator') do
206
+ |creator|
207
+ creator.display_seq = 2
208
+ end
209
+ book.add_creator 'another creator'
210
+ book.add_creator('first creator') do
211
+ |creator|
212
+ creator.display_seq = 1
213
+ end
214
+ book.add_creator('third creator') do
215
+ |creator|
216
+ creator.display_seq = 3
217
+ end
218
+ expect(book.creator_list[0].to_s).to eq('first creator')
219
+ expect(book.creator_list[1].to_s).to eq('second creator')
220
+ expect(book.creator_list[2].to_s).to eq('third creator')
221
+ expect(book.creator_list[3].to_s).to eq('a creator')
222
+ expect(book.creator_list[4].to_s).to eq('another creator')
223
+ end
224
+ end
225
+ end
226
+
227
+ # omit tests for setter/getter for contributor, publisher, date etc; these methods use same implementation as creator.
228
+
229
+ describe 'set_lastmodified' do
230
+ it 'set current time' do
231
+ book = GEPUB::Book.new
232
+ now = Time.now
233
+ book.set_lastmodified
234
+ expect((book.lastmodified.content - now).abs).to be < 2
235
+ end
236
+ it 'set time in string' do
237
+ book = GEPUB::Book.new
238
+ book.set_lastmodified(Time.parse('2012-9-12 00:00:00Z'))
239
+ expect(book.lastmodified.content).to eq(Time.parse('2012-9-12 00:00:00 UTC'))
240
+ end
241
+ end
242
+ describe 'page_progression_direction=' do
243
+ it 'set page_progression_direction' do
244
+ book = GEPUB::Book.new
245
+ book.page_progression_direction= 'rtl'
246
+ expect(book.page_progression_direction).to eq('rtl')
247
+ end
248
+ end
249
+ describe 'add_optional_file' do
250
+ context 'add apple specific option file' do
251
+ it 'is added to book' do
252
+ content = <<-EOF
253
+ <?xml version="1.0" encoding="UTF-8"?>
254
+ <display_options>
255
+ <platform name="*">
256
+ <option name="fixed-layout">true</option>
257
+ </platform>
258
+ </display_options>
259
+ EOF
260
+ book = GEPUB::Book.new
261
+ book.add_optional_file('META-INF/com.apple.ibooks.display-options.xm', StringIO.new(content))
262
+
263
+ expect(book.optional_files.size).to eq(1)
264
+ expect(book.optional_files['META-INF/com.apple.ibooks.display-options.xm']).to eq(content)
265
+ end
266
+ end
267
+ end
268
+ describe 'add_item' do
269
+ end
270
+ describe 'add_ordered_item' do
271
+ end
272
+ describe 'ordered' do
273
+ end
274
+ describe 'write_to_epub_container' do
275
+ context 'create typical book' do
276
+ end
277
+ context 'create very complex book' do
278
+ end
279
+ end
280
+ end
281
+ context 'on parsing existing book' do
282
+ describe '.parse' do
283
+ context 'IO Object' do
284
+ it 'loads book and returns GEPUB::Book object' do
285
+ filehandle = File.new(File.dirname(__FILE__) + '/fixtures/testdata/wasteland-20120118.epub')
286
+ book = GEPUB::Book.parse(filehandle)
287
+ expect(book).to be_instance_of GEPUB::Book
288
+ end
289
+ end
290
+ end
291
+ end
292
+ end
data/spec/builder_spec.rb CHANGED
@@ -356,6 +356,25 @@ describe GEPUB::Builder do
356
356
  builder.instance_eval{ @book.spine.itemref_list[1].properties[0] }.should == 'page-spread-right'
357
357
  end
358
358
 
359
+ it 'should add files and rendition property' do
360
+ workdir = File.join(File.dirname(__FILE__),'fixtures', 'builder')
361
+ builder = GEPUB::Builder.new {
362
+ resources(:workdir => workdir) {
363
+ ordered {
364
+ file('text/cover.xhtml')
365
+ file('text/memo.txt')
366
+ rendition_layout 'pre-paginated'
367
+ rendition_orientation 'landscape'
368
+ rendition_spread 'both'
369
+ }
370
+ }
371
+ }
372
+ builder.instance_eval{ @book.item_by_href('text/memo.txt') }.should_not be_nil
373
+ builder.instance_eval{ @book.spine.itemref_list[1].properties[0] }.should == 'rendition:layout-pre-paginated'
374
+ builder.instance_eval{ @book.spine.itemref_list[1].properties[1] }.should == 'rendition:orientation-landscape'
375
+ builder.instance_eval{ @book.spine.itemref_list[1].properties[2] }.should == 'rendition:spread-both'
376
+ end
377
+
359
378
  it 'should handle fallback chain' do
360
379
  workdir = File.join(File.dirname(__FILE__),'fixtures', 'builder')
361
380
  builder = GEPUB::Builder.new {
@@ -1,6 +1,11 @@
1
1
  <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2
2
 
3
- <package xmlns="http://www.idpf.org/2007/opf" version="3.0" unique-identifier="pub-id" xml:lang="ja">
3
+ <package xmlns="http://www.idpf.org/2007/opf" version="3.0"
4
+ unique-identifier="pub-id"
5
+ xml:lang="ja"
6
+ prefix="foaf: http://xmlns.com/foaf/spec/
7
+ rendition: http://www.idpf.org/vocab/rendition/#"
8
+ >
4
9
  <metadata xmlns:dc="http://purl.org/dc/elements/1.1/" >
5
10
  <dc:identifier id="pub-id">urn:uuid:1234567890</dc:identifier>
6
11
  <meta refines="#pub-id" property="identifier-type" scheme="xsd:string">uuid</meta>
@@ -34,6 +39,10 @@
34
39
 
35
40
  <meta property="dcterms:issued" id="issued">2012-02-12T15:00:00Z</meta>
36
41
  <meta property="dcterms:modified" id="modified">2012-02-12T19:00:00Z</meta>
42
+
43
+ <meta property="rendition:layout">pre-paginated</meta>
44
+ <meta property="rendition:spread">both</meta>
45
+
37
46
  <meta name="cover" content="cover-image" />
38
47
  </metadata>
39
48
 
@@ -57,4 +66,4 @@
57
66
  <itemref idref="nav" linear="no" />
58
67
  </spine>
59
68
 
60
- </package>
69
+ </package>
data/spec/package_spec.rb CHANGED
@@ -8,6 +8,7 @@ describe GEPUB::Package do
8
8
  opf = GEPUB::Package.new('/package.opf')
9
9
  opf.ns_prefix(GEPUB::XMLUtil::OPF_NS).should == 'xmlns'
10
10
  end
11
+
11
12
  context 'parse existing opf' do
12
13
  it 'should be initialized with opf' do
13
14
  opf = GEPUB::Package.parse_opf(File.open(File.dirname(__FILE__) + '/fixtures/testdata/test.opf'), '/package.opf')
@@ -15,8 +16,24 @@ describe GEPUB::Package do
15
16
  opf['version'].should == '3.0'
16
17
  opf['unique-identifier'].should == 'pub-id'
17
18
  opf['xml:lang'].should == 'ja'
19
+ opf['prefix'].should == 'foaf: http://xmlns.com/foaf/spec/ rendition: http://www.idpf.org/vocab/rendition/#'
20
+ end
21
+ it 'should parse prefix data' do
22
+ package = GEPUB::Package.parse_opf(File.open(File.dirname(__FILE__) + '/fixtures/testdata/test.opf'), '/package.opf')
23
+ package.prefixes.size.should == 2
24
+ package.prefixes['foaf'].should == 'http://xmlns.com/foaf/spec/'
25
+ package.prefixes['rendition'].should == 'http://www.idpf.org/vocab/rendition/#'
26
+
18
27
  end
19
-
28
+
29
+ it 'should parse rendition metadata' do
30
+ package = GEPUB::Package.parse_opf(File.open(File.dirname(__FILE__) + '/fixtures/testdata/test.opf'), '/package.opf')
31
+ package.rendition_layout.should == 'pre-paginated'
32
+ package.rendition_orientation.should == 'auto'
33
+ package.rendition_spread.should == 'both'
34
+
35
+ end
36
+
20
37
  end
21
38
  context 'generate new opf' do
22
39
  it 'should generate opf' do
@@ -58,6 +75,30 @@ describe GEPUB::Package do
58
75
  # TODO: should check all elements
59
76
  end
60
77
 
78
+ it 'should generate package with prefix attribute' do
79
+ package = GEPUB::Package.new('OEBPS/package.opf') do
80
+ |package|
81
+ package.set_main_id('http://example.jp', 'BookID', 'url')
82
+ package['xml:lang'] = 'ja'
83
+ package.enable_rendition
84
+ end
85
+ xml = Nokogiri::XML::Document.parse package.opf_xml
86
+ xml.root['prefix'].should == 'rendition: http://www.idpf.org/vocab/rendition/#'
87
+ end
88
+
89
+ it 'should generate package with rendition attributes' do
90
+ package = GEPUB::Package.new('OEBPS/package.opf') do
91
+ |package|
92
+ package.rendition_layout = 'pre-paginated'
93
+ package.rendition_orientation = 'portlait'
94
+ package.rendition_spread = 'landscape'
95
+ end
96
+ xml = Nokogiri::XML::Document.parse package.opf_xml
97
+ xml.at_xpath("//xmlns:meta[@property='rendition:layout']").content.should == 'pre-paginated'
98
+ xml.at_xpath("//xmlns:meta[@property='rendition:orientation']").content.should == 'portlait'
99
+ xml.at_xpath("//xmlns:meta[@property='rendition:spread']").content.should == 'landscape'
100
+ end
101
+
61
102
  it 'should generate opf2.0' do
62
103
  opf = GEPUB::Package.new('OEBPS/package.opf', { 'version' => '2.0'}) {
63
104
  |opf|
@@ -94,6 +135,5 @@ describe GEPUB::Package do
94
135
  xml.root['version'].should == '2.0'
95
136
  xml.root['lang'].should == 'ja'
96
137
  end
97
-
98
138
  end
99
139
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gepub
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.8.5
4
+ version: 0.6.8.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,22 +9,27 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-18 00:00:00.000000000 Z
12
+ date: 2012-12-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &70309442002400 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: '2'
21
+ version: '2.11'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70309442002400
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '2.11'
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: nokogiri
27
- requirement: &70309442001900 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ~>
@@ -32,10 +37,15 @@ dependencies:
32
37
  version: 1.5.0
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *70309442001900
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.5.0
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: rubyzip
38
- requirement: &70309442001440 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ~>
@@ -43,7 +53,12 @@ dependencies:
43
53
  version: 0.9.7
44
54
  type: :runtime
45
55
  prerelease: false
46
- version_requirements: *70309442001440
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 0.9.7
47
62
  description: gepub is a generic EPUB parser/generator. Generates and parse EPUB2 and
48
63
  EPUB3
49
64
  email:
@@ -76,13 +91,17 @@ files:
76
91
  - lib/gepub/item.rb
77
92
  - lib/gepub/manifest.rb
78
93
  - lib/gepub/meta.rb
94
+ - lib/gepub/meta_array.rb
79
95
  - lib/gepub/metadata.rb
80
96
  - lib/gepub/package.rb
97
+ - lib/gepub/rendition.rb
81
98
  - lib/gepub/resource_builder.rb
99
+ - lib/gepub/sorted_array.rb
82
100
  - lib/gepub/spine.rb
83
101
  - lib/gepub/version.rb
84
102
  - lib/gepub/xml_util.rb
85
103
  - spec/bindings_spec.rb
104
+ - spec/book_spec.rb
86
105
  - spec/builder_spec.rb
87
106
  - spec/example_spec.rb
88
107
  - spec/fixtures/builder/img/cover.jpg
@@ -108,6 +127,7 @@ files:
108
127
  - spec/fixtures/testdata/test.opf
109
128
  - spec/fixtures/testdata/test2.opf
110
129
  - spec/fixtures/testdata/test_with_bindings.opf
130
+ - spec/fixtures/testdata/wasteland-20120118.epub
111
131
  - spec/gepub_spec.rb
112
132
  - spec/manifest_spec.rb
113
133
  - spec/metadata_spec.rb
@@ -134,12 +154,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
134
154
  version: '0'
135
155
  requirements: []
136
156
  rubyforge_project: gepub
137
- rubygems_version: 1.8.11
157
+ rubygems_version: 1.8.23
138
158
  signing_key:
139
159
  specification_version: 3
140
160
  summary: a generic EPUB library for Ruby.
141
161
  test_files:
142
162
  - spec/bindings_spec.rb
163
+ - spec/book_spec.rb
143
164
  - spec/builder_spec.rb
144
165
  - spec/example_spec.rb
145
166
  - spec/fixtures/builder/img/cover.jpg
@@ -165,6 +186,7 @@ test_files:
165
186
  - spec/fixtures/testdata/test.opf
166
187
  - spec/fixtures/testdata/test2.opf
167
188
  - spec/fixtures/testdata/test_with_bindings.opf
189
+ - spec/fixtures/testdata/wasteland-20120118.epub
168
190
  - spec/gepub_spec.rb
169
191
  - spec/manifest_spec.rb
170
192
  - spec/metadata_spec.rb