gepub 0.6.8.5 → 0.6.8.6

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 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