epub-parser 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ [submodule "wiki"]
2
+ path = wiki
3
+ url = git://github.com/KitaitiMakoto/epub-parser.wiki.git
@@ -26,22 +26,30 @@ For more info:
26
26
  # do somethong...
27
27
  end
28
28
 
29
- See the [wiki][] for more info.
30
- Or [API Documentation][rubydoc].
29
+ See the [wiki][] or [API Documentation][rubydoc] for more info.
31
30
 
32
31
  [wiki]: https://github.com/KitaitiMakoto/epub-parser/wiki
33
32
  [rubydoc]: http://rubydoc.info/gems/epub-parser/frames
34
33
 
35
34
  REQUIREMENTS
36
35
  ------------
37
- * libzip for Zip/Ruby gem
38
36
  * libxml2 and libxslt for Nokogiri gem
39
37
 
38
+ CHANGELOG
39
+ ---------
40
+
41
+ ### 0.1.1
42
+ * Parse package@prefix and attach it as `Package#prefix`
43
+ * `Manifest::Item#iri` wes removed. It have existed for files in unzipped epub books but now EPUB Parser retrieves files from zip archive directly. `#href` now returns `Addressable::URI` object.
44
+ * `Metadata::Link#iri`: ditto.
45
+ * `Guide::Reference#iri`: ditto.
46
+
40
47
  TODOS
41
48
  -----
42
- * Adding tests
43
- * Modify methods around fallback to check bindings element in the package
49
+ * Vocabulary Association Mechanisms
44
50
  * Implementing navigation document and so on
51
+ * Fixed Layout
52
+ * Digital Signature
45
53
  * Using SAX on parsing
46
54
  * Extracting and organizing common behavior from some classes to modules
47
55
  * Abstraction of XML parser(making it possible to use REXML, standard bundled XML library of Ruby)
@@ -49,6 +57,7 @@ TODOS
49
57
  DONE
50
58
  ----
51
59
  * Using zip library instead of `unzip` command, which has security issue
60
+ * Modify methods around fallback to see `bindings` element in the package
52
61
 
53
62
  LICENSE
54
63
  -------
data/Rakefile CHANGED
@@ -1,8 +1,10 @@
1
- require 'bundler/gem_tasks'
1
+ require 'bundler/gem_helper'
2
2
  require 'rake/testtask'
3
+ require 'rake/clean'
3
4
  require 'yard'
4
5
  require 'cucumber'
5
6
  require 'cucumber/rake/task'
7
+ require 'epub/parser/version'
6
8
 
7
9
  task :default => :test
8
10
  task :test => 'test:default'
@@ -19,13 +21,31 @@ namespace :test do
19
21
  desc 'Build the test fixture EPUB'
20
22
  task :build do
21
23
  input_dir = 'test/fixtures/book'
22
- output_dir = 'test/fixtures/'
23
24
  FileList["#{input_dir}/**/*"]
24
25
  sh "epzip #{input_dir}"
25
26
  end
26
27
  end
27
28
 
28
29
 
29
- YARD::Rake::YardocTask.new
30
+ YARD::Rake::YardocTask.new do |task|
31
+ task.files = %w[- wiki/*.md]
32
+ end
33
+
34
+ gem_helper = Bundler::GemHelper.new
35
+
36
+ desc "Build epub-parser-#{EPUB::Parser::VERSION}.gem into the pkg directory."
37
+ task :build => :yard do
38
+ gem_helper.build_gem
39
+ end
40
+
41
+ desc "Build and install epub-parser-#{EPUB::Parser::VERSION}.gem into system gems."
42
+ task :install => :yard do
43
+ gem_helper.install_gem
44
+ end
45
+
46
+ desc "Create tag v#{EPUB::Parser::VERSION} and build and push epub-parser-#{EPUB::Parser::VERSION}.gem to Rubygems"
47
+ task :release => :yard do
48
+ gem_helper.release_gem
49
+ end
30
50
 
31
51
  Cucumber::Rake::Task.new
@@ -10,7 +10,10 @@ Usage: epubinfo [options] EPUBFILE
10
10
 
11
11
  EOB
12
12
  opt.version = EPUB::Parser::VERSION
13
- opt.on '-f', '--format=FORMAT', [:line, :json, :yaml], 'format of output(line, json or yaml), defaults to line(for console)' do |format|
13
+ formats = [:line, :json, :yaml]
14
+ nl_formats = formats.dup
15
+ nl_last = nl_formats.pop
16
+ opt.on '-f', '--format=FORMAT', formats, "format of output(#{nl_formats.join(', ')} or #{nl_last}), defaults to line(for console)" do |format|
14
17
  options[:format] = format
15
18
  end
16
19
  end
@@ -14,7 +14,9 @@ Gem::Specification.new do |s|
14
14
 
15
15
  # s.rubyforge_project = "epub-parser"
16
16
 
17
- s.files = `git ls-files`.split("\n").push('test/fixtures/book/OPS/ルートファイル.opf')
17
+ s.files = `git ls-files`.split("\n")
18
+ .push('test/fixtures/book/OPS/ルートファイル.opf')
19
+ .push(Dir['doc/*'])
18
20
  s.files.delete('"test/fixtures/book/OPS/\343\203\253\343\203\274\343\203\210\343\203\225\343\202\241\343\202\244\343\203\253.opf"')
19
21
  s.test_files = `git ls-files -- {test,spec,features}/**/*.rb`.split("\n")
20
22
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
@@ -20,14 +20,20 @@ module EPUB
20
20
  Parser.parse(file, options)
21
21
  end
22
22
 
23
+ Publication::Package::CONTENT_MODELS.each do |model|
24
+ define_method model do
25
+ package.__send__(model)
26
+ end
27
+ end
28
+
23
29
  %w[ title main_title subtitle short_title collection_title edition_title extended_title ].each do |met|
24
30
  define_method met do
25
- @package.metadata.__send__(met)
31
+ metadata.__send__(met)
26
32
  end
27
33
  end
28
34
 
29
35
  def each_page_on_spine(&blk)
30
- enum = @package.spine.items
36
+ enum = package.spine.items
31
37
  if block_given?
32
38
  enum.each &blk
33
39
  else
@@ -36,10 +42,11 @@ module EPUB
36
42
  end
37
43
 
38
44
  def each_page_on_toc(&blk)
45
+ raise NotImplementedError
39
46
  end
40
47
 
41
48
  def each_content(&blk)
42
- enum = @package.manifest.items
49
+ enum = manifest.items
43
50
  if block_given?
44
51
  enum.each &blk
45
52
  else
@@ -48,10 +55,11 @@ module EPUB
48
55
  end
49
56
 
50
57
  def other_navigation
58
+ raise NotImplementedError
51
59
  end
52
60
 
53
61
  def resources
54
- @package.manifest.items
62
+ manifest.items
55
63
  end
56
64
 
57
65
  # Syntax sugar
@@ -61,6 +69,6 @@ module EPUB
61
69
 
62
70
  # Syntax sugar
63
71
  def cover_image
64
- package.manifest.cover_image
72
+ manifest.cover_image
65
73
  end
66
74
  end
@@ -10,15 +10,10 @@ module EPUB
10
10
  'svg' => 'http://www.w3.org/2000/svg'
11
11
  }
12
12
 
13
- module Type
14
- TOC = 'toc'
15
- PAGE_LIST = 'page_list'
16
- LANDMARKS = 'landmarks'
17
- end
18
-
19
13
  module MediaType
20
14
  class UnsupportedError < StandardError; end
21
15
 
16
+ ROOTFILE = 'application/oebps-package+xml'
22
17
  IMAGE = %w[
23
18
  image/gif
24
19
  image/jpeg
@@ -1,6 +1,12 @@
1
1
  module EPUB
2
2
  module ContentDocument
3
3
  class Navigation
4
+ module Type
5
+ TOC = 'toc'
6
+ PAGE_LIST = 'page_list'
7
+ LANDMARKS = 'landmarks'
8
+ end
9
+
4
10
  attr_accessor :navs
5
11
  alias navigations navs
6
12
  alias navigations= navs=
@@ -3,7 +3,7 @@ module EPUB
3
3
  class Container
4
4
  FILE = 'container.xml'
5
5
 
6
- attr_accessor :rootfiles
6
+ attr_reader :rootfiles
7
7
 
8
8
  def initialize
9
9
  @rootfiles = []
@@ -16,6 +16,10 @@ module EPUB
16
16
 
17
17
  class Rootfile
18
18
  attr_accessor :full_path, :media_type
19
+
20
+ def initialize(full_path=nil, media_type=EPUB::MediaType::ROOTFILE)
21
+ @full_path, @media_type = full_path, media_type
22
+ end
19
23
  end
20
24
  end
21
25
  end
@@ -1,6 +1,7 @@
1
1
  module EPUB
2
2
  class OCF
3
3
  class Encryption
4
+ attr_accessor :content
4
5
  end
5
6
  end
6
7
  end
@@ -18,7 +18,7 @@ module EPUB
18
18
  # @param [Nokogiri::HTML::Document] document HTML document or element including nav
19
19
  # @return [Array<EPUB::ContentDocument::Navigation::Nav>] navs array of Nav object
20
20
  def parse_navigations(document)
21
- navs = document.search('/xhtml:html/xhtml:body//xhtml:nav', EPUB::NAMESPACES).collect {|elem| parse_navigation elem}
21
+ document.search('/xhtml:html/xhtml:body//xhtml:nav', EPUB::NAMESPACES).collect {|elem| parse_navigation elem}
22
22
  end
23
23
 
24
24
  # @param [Nokogiri::XML::Element] nav nav element
@@ -21,11 +21,14 @@ module EPUB
21
21
  end
22
22
 
23
23
  def parse
24
- container_xml = @zip.fopen(File.join(DIRECTORY, CONTAINER_FILE)).read
25
- @ocf.container = parse_container(container_xml)
26
- (EPUB::OCF::MODULES - ['container']).each do |m|
27
- @ocf.__send__ "#{m}=", __send__("parse_#{m}")
24
+ EPUB::OCF::MODULES.each do |m|
25
+ begin
26
+ file = @zip.fopen(File.join(DIRECTORY, self.class.const_get("#{m.upcase}_FILE")))
27
+ @ocf.__send__ "#{m}=", __send__("parse_#{m}", file.read)
28
+ rescue Zip::Error
29
+ end
28
30
  end
31
+
29
32
  @ocf
30
33
  end
31
34
 
@@ -43,8 +46,10 @@ module EPUB
43
46
  container
44
47
  end
45
48
 
46
- def parse_encryption
47
- warn "Not implemented: #{self.class}##{__method__}" if $VERBOSE
49
+ def parse_encryption(content)
50
+ encryption = EPUB::OCF::Encryption.new
51
+ encryption.content = content
52
+ encryption
48
53
  end
49
54
 
50
55
  def parse_manifest
@@ -1,3 +1,4 @@
1
+ require 'strscan'
1
2
  require 'zipruby'
2
3
  require 'nokogiri'
3
4
  require 'addressable/uri'
@@ -33,8 +34,12 @@ module EPUB
33
34
 
34
35
  def parse_package
35
36
  elem = @doc.root
36
- @package.version = elem['version']
37
- @package.unique_identifier_id = elem['unique-identifier']
37
+ %w[version xml:lang dir id].each do |attr|
38
+ writer = attr.gsub(/\:/, '_') + '='
39
+ @package.__send__(writer, elem[attr])
40
+ end
41
+ @unique_identifier_id = elem['unique-identifier']
42
+ @package.prefix = parse_prefix(elem['prefix'])
38
43
 
39
44
  @package
40
45
  end
@@ -45,10 +50,10 @@ module EPUB
45
50
  id_map = {}
46
51
 
47
52
  metadata.identifiers = elem.xpath('./dc:identifier', EPUB::NAMESPACES).collect do |e|
48
- identifier = EPUB::Publication::Package::Metadata::Identifier.new
53
+ identifier = EPUB::Publication::Package::Metadata::DCMES.new
49
54
  identifier.content = e.content
50
55
  identifier.id = id = e['id']
51
- metadata.unique_identifier = identifier if id == @package.unique_identifier_id
56
+ metadata.unique_identifier = identifier if id == @unique_identifier_id
52
57
 
53
58
  identifier
54
59
  end
@@ -95,10 +100,10 @@ module EPUB
95
100
 
96
101
  metadata.links = elem.xpath('./opf:link', EPUB::NAMESPACES).collect do |e|
97
102
  link = EPUB::Publication::Package::Metadata::Link.new
98
- %w[ href id media-type ].each do |attr|
103
+ %w[ id media-type ].each do |attr|
99
104
  link.__send__(attr.gsub(/-/, '_') + '=', e[attr])
100
105
  end
101
- link.iri = Addressable::URI.parse(e['href'])
106
+ link.href = Addressable::URI.parse(e['href'])
102
107
  link.rel = e['rel'].strip.split
103
108
  if (refines = e['refines']) && refines[0] == '#'
104
109
  id = refines[1..-1]
@@ -132,8 +137,7 @@ module EPUB
132
137
  %w[ id media-type media-overlay ].each do |attr|
133
138
  item.__send__("#{attr.gsub(/-/, '_')}=", e[attr])
134
139
  end
135
- item.href = e['href']
136
- item.iri = @rootfile.join Addressable::URI.parse(e['href'])
140
+ item.href = Addressable::URI.parse(e['href'])
137
141
  fallback_map[e['fallback']] = item if e['fallback']
138
142
  item.properties = e['properties'] ? e['properties'].split(' ') : []
139
143
  manifest << item
@@ -169,10 +173,10 @@ module EPUB
169
173
  guide = @package.guide = EPUB::Publication::Package::Guide.new
170
174
  @doc.xpath('/opf:package/opf:guide/opf:reference', EPUB::NAMESPACES).each do |ref|
171
175
  reference = EPUB::Publication::Package::Guide::Reference.new
172
- %w[ type title href ].each do |attr|
176
+ %w[ type title ].each do |attr|
173
177
  reference.__send__("#{attr}=", ref[attr])
174
178
  end
175
- reference.iri = @rootfile.join Addressable::URI.parse(reference.href)
179
+ reference.href = Addressable::URI.parse(ref['href'])
176
180
  guide << reference
177
181
  end
178
182
 
@@ -185,14 +189,31 @@ module EPUB
185
189
  media_type = EPUB::Publication::Package::Bindings::MediaType.new
186
190
  media_type.media_type = elem['media-type']
187
191
  items = @package.manifest.items
188
- index = items.index {|item| item.id == elem['handler']}
189
- media_type.handler = items[index] if index
192
+ media_type.handler = items.detect {|item| item.id == elem['handler']}
190
193
  bindings << media_type
191
194
  end
192
195
 
193
196
  bindings
194
197
  end
195
198
 
199
+ def parse_prefix(str)
200
+ prefixes = {}
201
+ return prefixes if str.nil? or str.empty?
202
+ scanner = StringScanner.new(str)
203
+ scanner.scan /\s*/
204
+ while prefix = scanner.scan(/[^\:\s]+/)
205
+ scanner.scan /[\:\s]+/
206
+ iri = scanner.scan(/[^\s]+/)
207
+ if iri.nil? or iri.empty?
208
+ warn "no IRI detected for prefix `#{prefix}`"
209
+ else
210
+ prefixes[prefix] = iri
211
+ end
212
+ scanner.scan /\s*/
213
+ end
214
+ prefixes
215
+ end
216
+
196
217
  def collect_dcmes(elem, selector)
197
218
  elem.xpath(selector, EPUB::NAMESPACES).collect do |e|
198
219
  md = EPUB::Publication::Package::Metadata::DCMES.new
@@ -1,5 +1,5 @@
1
1
  module EPUB
2
2
  class Parser
3
- VERSION = "0.1.0"
3
+ VERSION = "0.1.1"
4
4
  end
5
5
  end
@@ -1,6 +1 @@
1
1
  require 'epub/publication/package'
2
-
3
- module EPUB
4
- module Publication
5
- end
6
- end
@@ -1,37 +1,36 @@
1
- %w[ metadata manifest spine guide bindings ].each { |f| require "epub/publication/package/#{f}" }
2
-
3
1
  module EPUB
4
2
  module Publication
5
3
  class Package
6
- attr_accessor :book,
7
- :version, :unique_identifier_id, :prefix, :xml_lang, :dir, :id
8
- attr_reader :metadata, :manifest, :spine, :guide, :bindings
9
- alias lang xml_lang
10
- alias lang= xml_lang=
4
+ CONTENT_MODELS = [:metadata, :manifest, :spine, :guide, :bindings]
5
+ RESERVED_VOCABULARY_PREFIXES = {
6
+ '' => 'http://idpf.org/epub/vocab/package/#',
7
+ 'dcterms' => 'http://purl.org/dc/terms/',
8
+ 'marc' => 'http://id.loc.gov/vocabulary/',
9
+ 'media' => 'http://www.idpf.org/epub/vocab/overlays/#',
10
+ 'onix' => 'http://www.editeur.org/ONIX/book/codelists/current.html#',
11
+ 'xsd' => 'http://www.w3.org/2001/XMLSchema#'
12
+ }
11
13
 
12
- def metadata=(metadata)
13
- metadata.package = self
14
- @metadata = metadata
15
- end
16
-
17
- def manifest=(manifest)
18
- manifest.package = self
19
- @manifest = manifest
20
- end
21
14
 
22
- def spine=(spine)
23
- spine.package = self
24
- @spine = spine
15
+ class << self
16
+ def define_content_model(model_name)
17
+ define_method "#{model_name}=" do |model|
18
+ current_model = __send__(model_name)
19
+ current_model.package = nil if current_model
20
+ model.package = self
21
+ instance_variable_set "@#{model_name}", model
22
+ end
23
+ end
25
24
  end
26
25
 
27
- def guide=(guide)
28
- guide.package = self
29
- @guide = guide
30
- end
26
+ attr_accessor :book,
27
+ :version, :prefix, :xml_lang, :dir, :id
28
+ attr_reader *CONTENT_MODELS
29
+ alias lang xml_lang
30
+ alias lang= xml_lang=
31
31
 
32
- def bindings=(bindings)
33
- bindings.package = self
34
- @buindings = bindings
32
+ CONTENT_MODELS.each do |model|
33
+ define_content_model model
35
34
  end
36
35
 
37
36
  def unique_identifier
@@ -40,3 +39,7 @@ module EPUB
40
39
  end
41
40
  end
42
41
  end
42
+
43
+ EPUB::Publication::Package::CONTENT_MODELS.each do |f|
44
+ require_relative "package/#{f}"
45
+ end
@@ -2,12 +2,20 @@ module EPUB
2
2
  module Publication
3
3
  class Package
4
4
  class Bindings
5
- attr_accessor :package,
6
- :media_types
5
+ attr_accessor :package
7
6
 
8
7
  def <<(media_type)
9
- @media_types ||= []
10
- @media_types << media_type
8
+ @media_types ||= {}
9
+ @media_types[media_type.media_type] = media_type
10
+ end
11
+
12
+ def [](media_type)
13
+ _, mt = @media_types.detect {|key, _| key == media_type}
14
+ mt
15
+ end
16
+
17
+ def media_types
18
+ @media_types.values
11
19
  end
12
20
 
13
21
  class MediaType
@@ -16,26 +16,26 @@ module EPUB
16
16
  end
17
17
 
18
18
  %w[cover title-page toc index glossary acknowledgements bibliography colophon copyright-page dedication epigraph foreword loi lot notes preface text].each do |type|
19
- define_method type do
20
- var = instance_variable_get "@#{type}"
19
+ method_name = type.gsub('-', '_')
20
+ define_method method_name do
21
+ var = instance_variable_get "@#{method_name}"
21
22
  return var if var
22
23
 
23
- var = references.selector {|ref| ref.type == type.to_s}.first
24
- instance_variable_set "@#{type}", var
24
+ var = references.selector {|ref| ref.type == type}.first
25
+ instance_variable_set "@#{method_name}", var
25
26
  end
26
27
  end
27
28
 
28
29
  class Reference
29
30
  attr_accessor :guide,
30
- :type, :title, :href,
31
- :iri
31
+ :type, :title, :href
32
32
 
33
33
  def item
34
34
  return @item if @item
35
35
 
36
- len = iri.fragment.nil? ? 1 : iri.fragment.length + 2
36
+ request_uri = href.request_uri
37
37
  @item = @guide.package.manifest.items.selector do |item|
38
- item.href == href[0 .. -len]
38
+ item.href.request_uri == request_uri
39
39
  end.first
40
40
  end
41
41
  end
@@ -28,7 +28,7 @@ module EPUB
28
28
  end
29
29
 
30
30
  def items
31
- @items.collect {|id, item| item}
31
+ @items.values
32
32
  end
33
33
 
34
34
  def [](item_id)
@@ -36,23 +36,37 @@ module EPUB
36
36
  end
37
37
 
38
38
  class Item
39
+ # @!attribute [rw] manifest
40
+ # @return [Manifest] Returns the value of manifest
41
+ # @!attribute [rw] id
42
+ # @return [String] Returns the value of id
43
+ # @!attribute [rw] href
44
+ # @return [Addressable::URI] Returns the value of href
45
+ # @!attribute [rw] media_type
46
+ # @return [String] Returns the value of media_type
47
+ # @!attribute [rw] properties
48
+ # @return [Array<String>] Returns the value of properties
49
+ # @!attribute [rw] media_overlay
50
+ # @return [String] Returns the value of media_overlay
51
+ # @!attribute [rw] fallback
52
+ # @return [Item] Returns the value of attribute fallback
39
53
  attr_accessor :manifest,
40
- :id, :href, :media_type, :fallback, :properties, :media_overlay,
41
- :iri
54
+ :id, :href, :media_type, :fallback, :properties, :media_overlay
42
55
 
43
- # To do: Handle circular fallback chain
56
+ # @todo Handle circular fallback chain
44
57
  def fallback_chain
45
- return @fallback_chain if @fallback_chain
46
- @fallback_chain = traverse_fallback_chain([])
58
+ @fallback_chain ||= traverse_fallback_chain([])
47
59
  end
48
60
 
49
61
  def read
62
+ rootfile = Addressable::URI.parse(manifest.package.book.ocf.container.rootfile.full_path)
50
63
  Zip::Archive.open(manifest.package.book.epub_file) {|zip|
51
- zip.fopen(iri.to_s).read
64
+ path = rootfile + href.request_uri
65
+ zip.fopen(path.to_s).read
52
66
  }
53
67
  end
54
68
 
55
- # To do: Handle circular fallback chain
69
+ # @todo Handle circular fallback chain
56
70
  def use_fallback_chain(options = {})
57
71
  supported = EPUB::MediaType::CORE
58
72
  if ad = options[:supported]
@@ -62,13 +76,12 @@ module EPUB
62
76
  supported = supported - (del.respond_to?(:to_ary) ? del : [del])
63
77
  end
64
78
 
65
- if supported.include? media_type
66
- yield self
67
- elsif fallback
68
- fallback.use_fallback_chain(options) {|fb| yield fb}
69
- else
70
- raise EPUB::MediaType::UnsupportedError
79
+ return yield self if supported.include? media_type
80
+ if (bindings = manifest.package.bindings) && (binding_media_type = bindings[media_type])
81
+ return yield binding_media_type.handler
71
82
  end
83
+ return fallback.use_fallback_chain(options) {|fb| yield fb} if fallback
84
+ raise EPUB::MediaType::UnsupportedError
72
85
  end
73
86
 
74
87
  protected
@@ -12,6 +12,12 @@ module EPUB
12
12
  alias_method "#{elem}=", "dc_#{elem}="
13
13
  end
14
14
 
15
+ def initialize
16
+ (DC_ELEMS + [:metas, :links]).each do |elem|
17
+ __send__ "#{elem}=", []
18
+ end
19
+ end
20
+
15
21
  def title
16
22
  return extended_title unless extended_title.empty?
17
23
  compositted = titles.select {|title| title.display_seq}.sort.join("\n")
@@ -41,14 +47,14 @@ module EPUB
41
47
  end
42
48
 
43
49
  module Refinable
50
+ PROPERTIES = %w[ alternate-script display-seq file-as group-position identifier-type meta-auth role title-type ]
51
+
44
52
  attr_writer :refiners
45
53
 
46
54
  def refiners
47
55
  @refiners ||= []
48
56
  end
49
57
 
50
- PROPERTIES = %w[ alternate-script display-seq file-as group-position identifier-type meta-auth role title-type ]
51
-
52
58
  PROPERTIES.each do |voc|
53
59
  met = voc.gsub(/-/, '_')
54
60
  attr_writer met
@@ -58,41 +64,24 @@ module EPUB
58
64
  end
59
65
  end
60
66
 
61
- class Identifier
67
+ class DCMES
62
68
  include Refinable
63
69
 
64
- attr_accessor :content, :id
70
+ attr_accessor :content, :id, :lang, :dir
65
71
 
66
72
  def to_s
67
73
  content
68
74
  end
69
75
  end
70
76
 
71
- class Title
72
- include Refinable
77
+ class Title < DCMES
73
78
  include Comparable
74
79
 
75
- attr_accessor :content, :id, :lang, :dir
76
-
77
80
  def <=>(other)
78
81
  return 1 if other.display_seq.nil?
79
82
  return -1 if display_seq.nil?
80
83
  display_seq.to_s.to_i <=> other.display_seq.to_s.to_i
81
84
  end
82
-
83
- def to_s
84
- content
85
- end
86
- end
87
-
88
- class DCMES
89
- include Refinable
90
-
91
- attr_accessor :content, :id, :lang, :dir
92
-
93
- def to_s
94
- content
95
- end
96
85
  end
97
86
 
98
87
  class Meta
@@ -118,8 +107,7 @@ module EPUB
118
107
  class Link
119
108
  include Refinable
120
109
 
121
- attr_accessor :href, :rel, :id, :refines, :media_type,
122
- :iri
110
+ attr_accessor :href, :rel, :id, :refines, :media_type
123
111
  end
124
112
  end
125
113
  end
@@ -40,6 +40,12 @@ module EPUB
40
40
  def item
41
41
  @item ||= @spine.package.manifest[idref]
42
42
  end
43
+
44
+ def ==(other)
45
+ [:spine, :idref, :linear, :id].all? {|meth|
46
+ self.__send__(meth) == other.__send__(meth)
47
+ } and (other.properties - properties).empty?
48
+ end
43
49
  end
44
50
  end
45
51
  end
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE html>
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
4
+ <head>
5
+ <title>LARGE FILE NAME</title>
6
+ </head>
7
+ <body>
8
+ </body>
9
+ </html>
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE html>
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
4
+ <head>
5
+ <title>small file name</title>
6
+ </head>
7
+ <body>
8
+ </body>
9
+ </html>
@@ -1,7 +1,9 @@
1
1
  <?xml version="1.0"?>
2
2
  <package version="3.0"
3
3
  unique-identifier="pub-id"
4
- xmlns="http://www.idpf.org/2007/opf">
4
+ xmlns="http://www.idpf.org/2007/opf"
5
+ prefix=" foaf: http://xmlns.com/foaf/spec/
6
+ dbp: http://dbpedia.org/ontology/ ">
5
7
  <metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
6
8
  <dc:identifier id="pub-id">da265185-8da8-462d-a146-17dd388f61fc</dc:identifier>
7
9
 
@@ -57,6 +59,12 @@
57
59
  href="item-2.xhtml"
58
60
  media-type="application/pdf"
59
61
  fallback="manifest-item-fallback"/>
62
+ <item id="large-file-name"
63
+ href="CASE-SENSITIVE.xhtml"
64
+ media-type="application/xhtml+xml"/>
65
+ <item id="small-file-name"
66
+ href="case-sensitive.xhtml"
67
+ media-type="application/xhtml+xml"/>
60
68
  <item id="impl"
61
69
  href="impl.xhtml"
62
70
  media-type="application/xhtml+xml"
@@ -24,6 +24,12 @@ class TestParserOCF < Test::Unit::TestCase
24
24
  assert_equal 'OPS/ルートファイル.opf', container.rootfile.full_path
25
25
  end
26
26
 
27
+ def test_parse_encryption_do_nothing_excluding_to_have_content
28
+ encryption = @parser.parse_encryption('content')
29
+
30
+ assert_equal 'content', encryption.content
31
+ end
32
+
27
33
  def test_parse
28
34
  assert_nothing_raised do
29
35
  @parser.parse
@@ -20,6 +20,17 @@ class TestParserPublication < Test::Unit::TestCase
20
20
  assert_equal '3.0', @package.version
21
21
  end
22
22
 
23
+ def test_has_unique_identifier
24
+ @package.metadata = @parser.parse_metadata
25
+ assert_equal 'pub-id', @package.unique_identifier.id
26
+ assert_equal 'da265185-8da8-462d-a146-17dd388f61fc', @package.unique_identifier.to_s
27
+ end
28
+
29
+ def test_has_prefixes_for_vocabulary
30
+ assert_equal ({'foaf' => 'http://xmlns.com/foaf/spec/', 'dbp' => 'http://dbpedia.org/ontology/'}),
31
+ @package.prefix
32
+ end
33
+
23
34
  class TestParseMetadata < TestParserPublication
24
35
  def setup
25
36
  super
@@ -37,6 +48,21 @@ class TestParserPublication < Test::Unit::TestCase
37
48
  def test_has_five_titles
38
49
  assert_equal 5, @metadata.titles.length
39
50
  end
51
+
52
+ def test_returns_extended_title_as_title_attribute_if_exists
53
+ assert_equal 'The Great Cookbooks of the World:
54
+ Mon premier guide de cuisson, un Mémoire.
55
+ The New French Cuisine Masters, Volume Two.
56
+ Special Anniversary Edition', @metadata.title
57
+ end
58
+
59
+ def test_titles_has_order
60
+ titles = @metadata.titles
61
+ assert titles[0] > titles[1]
62
+ assert titles[1] < titles[2]
63
+ assert titles[2] < titles[3]
64
+ assert titles[3] > titles[4]
65
+ end
40
66
  end
41
67
 
42
68
  class TestParseManifest < TestParserPublication
@@ -45,12 +71,12 @@ class TestParserPublication < Test::Unit::TestCase
45
71
  @manifest = @parser.parse_manifest
46
72
  end
47
73
 
48
- def test_manifest_has_10_items
49
- assert_equal 10, @manifest.items.length
74
+ def test_manifest_has_12_items
75
+ assert_equal 12, @manifest.items.length
50
76
  end
51
77
 
52
- def test_item_has_relative_path_as_iri_attribute
53
- assert_equal 'OPS/nav.xhtml', @manifest['nav'].iri.to_s
78
+ def test_item_has_relative_path_as_href_attribute
79
+ assert_equal 'nav.xhtml', @manifest['nav'].href.to_s
54
80
  end
55
81
 
56
82
  def test_fallback_attribute_of_item_should_be_item_object
@@ -61,16 +87,18 @@ class TestParserPublication < Test::Unit::TestCase
61
87
  end
62
88
 
63
89
  def test_item_is_readable
64
- book = Object.new
65
- mock(@package).book {book}
66
- mock(book).epub_file {'test/fixtures/book.epub'}
67
-
68
- item = @manifest.items.first
90
+ item = EPUB::Parser.parse('test/fixtures/book.epub').package.manifest.items.first
69
91
  doc = Nokogiri.XML item.read
70
92
 
71
93
  assert_equal 'html', doc.root.name
72
94
  end
73
95
 
96
+ def test_iri_of_item_is_case_sensitive
97
+ manifest = EPUB::Parser.parse('test/fixtures/book.epub').package.manifest
98
+
99
+ assert_not_equal manifest['large-file-name'].read, manifest['small-file-name'].read
100
+ end
101
+
74
102
  def test_item_can_traverse_fallback_chain
75
103
  assert_equal [@manifest['manifest-item-2'], @manifest['manifest-item-fallback'], @manifest['manifest-item-fallback2']],
76
104
  @manifest['manifest-item-2'].fallback_chain
@@ -113,7 +141,27 @@ class TestParserPublication < Test::Unit::TestCase
113
141
 
114
142
  def test_item_with_absolute_iri_as_href_must_keep_it
115
143
  item = @manifest['external-css']
116
- assert_equal 'http://example.net/stylesheets/common.css', item.iri.to_s
144
+ assert_equal 'http://example.net/stylesheets/common.css', item.href.to_s
145
+ end
146
+ end
147
+
148
+ class TestParseSpine < TestParserPublication
149
+ def setup
150
+ super
151
+ @spine = @parser.parse_spine
152
+ end
153
+
154
+ def test_each_itemref_yields_itemref_in_order_on_spine_element
155
+ expected = %w[nav manifest-item-1 manifest-item-2].map {|idref|
156
+ itemref = EPUB::Publication::Package::Spine::Itemref.new
157
+ itemref.id = nil
158
+ itemref.spine = @spine
159
+ itemref.idref = idref
160
+ itemref.linear = true
161
+ itemref.properties = []
162
+ itemref
163
+ }
164
+ assert_equal expected, @spine.each_itemref.to_a
117
165
  end
118
166
  end
119
167
 
@@ -155,6 +203,12 @@ class TestParserPublication < Test::Unit::TestCase
155
203
  assert_equal 1, @bindings.media_types.length
156
204
  end
157
205
 
206
+ def test_bindings_is_accessible_like_hash
207
+ media_type = @bindings.media_types.first
208
+ assert_equal media_type, @bindings['application/x-demo-slideshow']
209
+ assert_nil @bindings['non-existing-media-type']
210
+ end
211
+
158
212
  def test_media_type_has_media_type_attribute
159
213
  assert_equal 'application/x-demo-slideshow', @bindings.media_types.first.media_type
160
214
  end
@@ -166,5 +220,12 @@ class TestParserPublication < Test::Unit::TestCase
166
220
  def test_media_type_refers_item_as_handler
167
221
  assert_kind_of EPUB::Publication::Package::Manifest::Item, @bindings.media_types.first.handler
168
222
  end
223
+
224
+ def test_use_fallback_chain_use_bindings
225
+ item = @package.manifest['slideshow']
226
+ item.use_fallback_chain do |slideshow|
227
+ assert_equal 'impl', slideshow.id
228
+ end
229
+ end
169
230
  end
170
231
  end
@@ -0,0 +1,16 @@
1
+ require_relative 'helper'
2
+ require 'epub/publication'
3
+
4
+ class TestPublication < Test::Unit::TestCase
5
+ def test_package_clear_package_attribute_of_submodules_when_attribute_writer_called
6
+ metadata = EPUB::Publication::Package::Metadata.new
7
+ another_metadata = EPUB::Publication::Package::Metadata.new
8
+ package = EPUB::Publication::Package.new
9
+
10
+ package.metadata = metadata
11
+ assert_equal metadata.package, package
12
+
13
+ package.metadata = another_metadata
14
+ assert_nil metadata.package
15
+ end
16
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: epub-parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-23 00:00:00.000000000Z
12
+ date: 2012-12-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rubygems-test
16
- requirement: &14556200 !ruby/object:Gem::Requirement
16
+ requirement: &14987420 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *14556200
24
+ version_requirements: *14987420
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &14555160 !ruby/object:Gem::Requirement
27
+ requirement: &14986340 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *14555160
35
+ version_requirements: *14986340
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: pry
38
- requirement: &14553880 !ruby/object:Gem::Requirement
38
+ requirement: &14984920 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *14553880
46
+ version_requirements: *14984920
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: pry-doc
49
- requirement: &14552820 !ruby/object:Gem::Requirement
49
+ requirement: &15155000 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *14552820
57
+ version_requirements: *15155000
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: test-unit-full
60
- requirement: &14835840 !ruby/object:Gem::Requirement
60
+ requirement: &15152760 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *14835840
68
+ version_requirements: *15152760
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: simplecov
71
- requirement: &14832360 !ruby/object:Gem::Requirement
71
+ requirement: &15151200 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '0'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *14832360
79
+ version_requirements: *15151200
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: thin
82
- requirement: &14761180 !ruby/object:Gem::Requirement
82
+ requirement: &15148720 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: '0'
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *14761180
90
+ version_requirements: *15148720
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: yard
93
- requirement: &14760040 !ruby/object:Gem::Requirement
93
+ requirement: &15163320 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,10 +98,10 @@ dependencies:
98
98
  version: '0'
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *14760040
101
+ version_requirements: *15163320
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: gem-man
104
- requirement: &14758040 !ruby/object:Gem::Requirement
104
+ requirement: &15162500 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ! '>='
@@ -109,10 +109,10 @@ dependencies:
109
109
  version: '0'
110
110
  type: :development
111
111
  prerelease: false
112
- version_requirements: *14758040
112
+ version_requirements: *15162500
113
113
  - !ruby/object:Gem::Dependency
114
114
  name: ronn
115
- requirement: &14756820 !ruby/object:Gem::Requirement
115
+ requirement: &15161460 !ruby/object:Gem::Requirement
116
116
  none: false
117
117
  requirements:
118
118
  - - ! '>='
@@ -120,10 +120,10 @@ dependencies:
120
120
  version: '0'
121
121
  type: :development
122
122
  prerelease: false
123
- version_requirements: *14756820
123
+ version_requirements: *15161460
124
124
  - !ruby/object:Gem::Dependency
125
125
  name: epzip
126
- requirement: &14791880 !ruby/object:Gem::Requirement
126
+ requirement: &15160700 !ruby/object:Gem::Requirement
127
127
  none: false
128
128
  requirements:
129
129
  - - ! '>='
@@ -131,10 +131,10 @@ dependencies:
131
131
  version: '0'
132
132
  type: :development
133
133
  prerelease: false
134
- version_requirements: *14791880
134
+ version_requirements: *15160700
135
135
  - !ruby/object:Gem::Dependency
136
136
  name: epubcheck
137
- requirement: &14790180 !ruby/object:Gem::Requirement
137
+ requirement: &15160160 !ruby/object:Gem::Requirement
138
138
  none: false
139
139
  requirements:
140
140
  - - ! '>='
@@ -142,10 +142,10 @@ dependencies:
142
142
  version: '0'
143
143
  type: :development
144
144
  prerelease: false
145
- version_requirements: *14790180
145
+ version_requirements: *15160160
146
146
  - !ruby/object:Gem::Dependency
147
147
  name: epub_validator
148
- requirement: &14789060 !ruby/object:Gem::Requirement
148
+ requirement: &15159440 !ruby/object:Gem::Requirement
149
149
  none: false
150
150
  requirements:
151
151
  - - ! '>='
@@ -153,10 +153,10 @@ dependencies:
153
153
  version: '0'
154
154
  type: :development
155
155
  prerelease: false
156
- version_requirements: *14789060
156
+ version_requirements: *15159440
157
157
  - !ruby/object:Gem::Dependency
158
158
  name: aruba
159
- requirement: &14786300 !ruby/object:Gem::Requirement
159
+ requirement: &15158500 !ruby/object:Gem::Requirement
160
160
  none: false
161
161
  requirements:
162
162
  - - ! '>='
@@ -164,10 +164,10 @@ dependencies:
164
164
  version: '0'
165
165
  type: :development
166
166
  prerelease: false
167
- version_requirements: *14786300
167
+ version_requirements: *15158500
168
168
  - !ruby/object:Gem::Dependency
169
169
  name: enumerabler
170
- requirement: &13965260 !ruby/object:Gem::Requirement
170
+ requirement: &15157640 !ruby/object:Gem::Requirement
171
171
  none: false
172
172
  requirements:
173
173
  - - ! '>='
@@ -175,10 +175,10 @@ dependencies:
175
175
  version: '0'
176
176
  type: :runtime
177
177
  prerelease: false
178
- version_requirements: *13965260
178
+ version_requirements: *15157640
179
179
  - !ruby/object:Gem::Dependency
180
180
  name: zipruby
181
- requirement: &13963160 !ruby/object:Gem::Requirement
181
+ requirement: &15157120 !ruby/object:Gem::Requirement
182
182
  none: false
183
183
  requirements:
184
184
  - - ! '>='
@@ -186,10 +186,10 @@ dependencies:
186
186
  version: '0'
187
187
  type: :runtime
188
188
  prerelease: false
189
- version_requirements: *13963160
189
+ version_requirements: *15157120
190
190
  - !ruby/object:Gem::Dependency
191
191
  name: nokogiri
192
- requirement: &13962300 !ruby/object:Gem::Requirement
192
+ requirement: &15156560 !ruby/object:Gem::Requirement
193
193
  none: false
194
194
  requirements:
195
195
  - - ! '>='
@@ -197,10 +197,10 @@ dependencies:
197
197
  version: '0'
198
198
  type: :runtime
199
199
  prerelease: false
200
- version_requirements: *13962300
200
+ version_requirements: *15156560
201
201
  - !ruby/object:Gem::Dependency
202
202
  name: addressable
203
- requirement: &13960940 !ruby/object:Gem::Requirement
203
+ requirement: &15155700 !ruby/object:Gem::Requirement
204
204
  none: false
205
205
  requirements:
206
206
  - - ! '>='
@@ -208,7 +208,7 @@ dependencies:
208
208
  version: '0'
209
209
  type: :runtime
210
210
  prerelease: false
211
- version_requirements: *13960940
211
+ version_requirements: *15155700
212
212
  description: Parse EPUB 3 book loosely
213
213
  email:
214
214
  - KitaitiMakoto@gmail.com
@@ -219,6 +219,7 @@ extra_rdoc_files: []
219
219
  files:
220
220
  - .gemtest
221
221
  - .gitignore
222
+ - .gitmodules
222
223
  - Gemfile
223
224
  - MIT-LICENSE
224
225
  - README.markdown
@@ -255,6 +256,8 @@ files:
255
256
  - schemas/epub-xhtml-30.sch
256
257
  - schemas/ocf-container-30.rnc
257
258
  - test/fixtures/book/META-INF/container.xml
259
+ - test/fixtures/book/OPS/CASE-SENSITIVE.xhtml
260
+ - test/fixtures/book/OPS/case-sensitive.xhtml
258
261
  - test/fixtures/book/OPS/nav.xhtml
259
262
  - test/fixtures/book/mimetype
260
263
  - test/helper.rb
@@ -263,7 +266,21 @@ files:
263
266
  - test/test_parser_content_document.rb
264
267
  - test/test_parser_ocf.rb
265
268
  - test/test_parser_publication.rb
269
+ - test/test_publication.rb
266
270
  - test/fixtures/book/OPS/ルートファイル.opf
271
+ - doc/method_list.html
272
+ - doc/EPUB.html
273
+ - doc/file.README.html
274
+ - doc/file.Item.html
275
+ - doc/index.html
276
+ - doc/AcceptHashAndYieldSlef.html
277
+ - doc/file.Home.html
278
+ - doc/_index.html
279
+ - doc/file_list.html
280
+ - doc/AcceptHashAndYieldSelf.html
281
+ - doc/frames.html
282
+ - doc/class_list.html
283
+ - doc/top-level-namespace.html
267
284
  homepage: https://github.com/KitaitiMakoto/epub-parser
268
285
  licenses:
269
286
  - MIT
@@ -279,7 +296,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
279
296
  version: '0'
280
297
  segments:
281
298
  - 0
282
- hash: -473258669036225186
299
+ hash: 2655157139786111314
283
300
  required_rubygems_version: !ruby/object:Gem::Requirement
284
301
  none: false
285
302
  requirements:
@@ -288,7 +305,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
288
305
  version: '0'
289
306
  segments:
290
307
  - 0
291
- hash: -473258669036225186
308
+ hash: 2655157139786111314
292
309
  requirements: []
293
310
  rubyforge_project:
294
311
  rubygems_version: 1.8.8