gepub 0.5.0 → 0.6.0
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/.gitignore +3 -1
- data/.travis.yml +6 -0
- data/README.rdoc +9 -55
- data/bin/gepuber +11 -6
- data/examples/generate_example.rb +52 -0
- data/examples/image1.jpg +0 -0
- data/gepub.gemspec +3 -4
- data/lib/gepub/book.rb +154 -176
- data/lib/gepub/gepuber.rb +6 -2
- data/lib/gepub/item.rb +66 -7
- data/lib/gepub/manifest.rb +78 -0
- data/lib/gepub/meta.rb +119 -0
- data/lib/gepub/metadata.rb +230 -0
- data/lib/gepub/package.rb +232 -0
- data/lib/gepub/spine.rb +112 -0
- data/lib/gepub/version.rb +1 -1
- data/lib/gepub/xml_util.rb +25 -0
- data/lib/gepub.rb +16 -0
- data/spec/example_spec.rb +53 -0
- data/spec/fixtures/epubcheck-3.0b4/COPYING.txt +19 -0
- data/spec/fixtures/epubcheck-3.0b4/README.txt +61 -0
- data/spec/fixtures/epubcheck-3.0b4/epubcheck-3.0b4.jar +0 -0
- data/spec/fixtures/epubcheck-3.0b4/jing_license.txt +12 -0
- data/spec/fixtures/epubcheck-3.0b4/lib/commons-compress-1.2.jar +0 -0
- data/spec/fixtures/epubcheck-3.0b4/lib/cssparser-0.9.6.jar +0 -0
- data/spec/fixtures/epubcheck-3.0b4/lib/jing.jar +0 -0
- data/spec/fixtures/epubcheck-3.0b4/lib/sac-1.3.jar +0 -0
- data/spec/fixtures/epubcheck-3.0b4/lib/saxon9he.jar +0 -0
- data/spec/fixtures/testdata/image1.jpg +0 -0
- data/spec/fixtures/testdata/test.opf +60 -0
- data/spec/gepub_spec.rb +57 -17
- data/spec/gepuber_spec.rb +8 -8
- data/spec/manifest_spec.rb +36 -0
- data/spec/metadata_spec.rb +170 -0
- data/spec/package_spec.rb +98 -0
- data/spec/spine_spec.rb +41 -0
- metadata +53 -23
- data/examples/example.rb +0 -49
data/lib/gepub/item.rb
CHANGED
@@ -1,21 +1,76 @@
|
|
1
1
|
module GEPUB
|
2
2
|
class Item
|
3
|
-
attr_accessor :
|
3
|
+
attr_accessor :content
|
4
|
+
def self.create(parent, attributes = {})
|
5
|
+
Item.new(attributes['id'], attributes['href'], attributes['media-type'], parent,
|
6
|
+
attributes.reject { |k,v| ['id','href','media-type'].member?(k) })
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(itemid, itemhref, itemmediatype = nil, parent = nil, attributes = {})
|
10
|
+
if attributes['properties'].class == String
|
11
|
+
attributes['properties'] = attributes['properties'].split(' ')
|
12
|
+
end
|
13
|
+
@attributes = {'id' => itemid, 'href' => itemhref, 'media-type' => itemmediatype}.merge(attributes)
|
14
|
+
@attributes['media-type'] = guess_mediatype if media_type.nil?
|
15
|
+
@parent = parent
|
16
|
+
@parent.register_item(self) unless @parent.nil?
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
['id', 'href', 'media-type', 'fallback', 'properties', 'media-overlay'].each { |name|
|
21
|
+
methodbase = name.sub('-','_')
|
22
|
+
define_method(methodbase + '=') { |val| @attributes[name] = val }
|
23
|
+
define_method('set_' + methodbase) { |val| @attributes[name] = val }
|
24
|
+
define_method(methodbase) { @attributes[name] }
|
25
|
+
}
|
26
|
+
|
27
|
+
def itemid
|
28
|
+
id
|
29
|
+
end
|
30
|
+
|
31
|
+
def mediatype
|
32
|
+
media_type
|
33
|
+
end
|
34
|
+
|
35
|
+
def [](x)
|
36
|
+
@attributes[x]
|
37
|
+
end
|
4
38
|
|
5
|
-
def
|
6
|
-
@
|
7
|
-
|
8
|
-
|
39
|
+
def []=(x,y)
|
40
|
+
@attributes[x] = y
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_property(property)
|
44
|
+
(@attributes['properties'] ||=[]) << property
|
45
|
+
self
|
9
46
|
end
|
10
47
|
|
48
|
+
def cover_image
|
49
|
+
add_property('cover-image')
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_raw_content(data)
|
53
|
+
@content = data
|
54
|
+
end
|
11
55
|
def add_content(io)
|
12
56
|
io.binmode
|
13
57
|
@content = io.read
|
14
58
|
self
|
15
59
|
end
|
16
|
-
|
60
|
+
|
61
|
+
def to_xml(builder, opf_version = '3.0')
|
62
|
+
attr = @attributes.dup
|
63
|
+
if opf_version.to_f < 3.0
|
64
|
+
attr.reject!{ |k,v| k == 'properties' }
|
65
|
+
end
|
66
|
+
if !attr['properties'].nil?
|
67
|
+
attr['properties'] = attr['properties'].join(' ')
|
68
|
+
end
|
69
|
+
builder.item(attr)
|
70
|
+
end
|
71
|
+
|
17
72
|
def guess_mediatype
|
18
|
-
case File.extname(
|
73
|
+
case File.extname(href)
|
19
74
|
when /.(html|xhtml)/i
|
20
75
|
'application/xhtml+xml'
|
21
76
|
when /.css/i
|
@@ -34,6 +89,10 @@ module GEPUB
|
|
34
89
|
'application/oebps-package+xml'
|
35
90
|
when /.ncx/i
|
36
91
|
'application/x-dtbncx+xml'
|
92
|
+
when /.(otf|ttf|ttc)/i
|
93
|
+
'application/vnd.ms-opentype'
|
94
|
+
when /.woff/i
|
95
|
+
'application/font-woff'
|
37
96
|
end
|
38
97
|
end
|
39
98
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'nokogiri'
|
3
|
+
module GEPUB
|
4
|
+
class Manifest
|
5
|
+
include XMLUtil
|
6
|
+
attr_accessor :opf_version
|
7
|
+
def self.parse(manifest_xml, opf_version = '3.0', id_pool = Package::IDPool.new)
|
8
|
+
Manifest.new(opf_version, id_pool) {
|
9
|
+
|manifest|
|
10
|
+
manifest.instance_eval {
|
11
|
+
@xml = manifest_xml
|
12
|
+
@namespaces = @xml.namespaces
|
13
|
+
@attributes = attr_to_hash(@xml.attributes)
|
14
|
+
@items = {}
|
15
|
+
@items_by_href = {}
|
16
|
+
@xml.xpath("//#{ns_prefix(OPF_NS)}:manifest/#{ns_prefix(OPF_NS)}:item", @namespaces).map {
|
17
|
+
|item|
|
18
|
+
i = Item.create(self, attr_to_hash(item.attributes))
|
19
|
+
@items[i.id] = i
|
20
|
+
@items_by_href[i.href] = i
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def id=(val)
|
27
|
+
@attributes['id'] = val
|
28
|
+
end
|
29
|
+
|
30
|
+
def id
|
31
|
+
@attributes['id']
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(opf_version = '3.0', id_pool = Package::IDPool.new)
|
35
|
+
@id_pool = id_pool
|
36
|
+
@attributes = {}
|
37
|
+
@items = {}
|
38
|
+
@items_by_href = {}
|
39
|
+
@opf_version = opf_version
|
40
|
+
yield self if block_given?
|
41
|
+
end
|
42
|
+
|
43
|
+
def item_list
|
44
|
+
@items.dup
|
45
|
+
end
|
46
|
+
|
47
|
+
def item_by_href(href)
|
48
|
+
@items_by_href[href]
|
49
|
+
end
|
50
|
+
|
51
|
+
def add_item(id,href,media_type, attributes = {})
|
52
|
+
@items[id] = item = Item.new(id,href,media_type,self, attributes)
|
53
|
+
@items_by_href[href] = item
|
54
|
+
item
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_xml(builder)
|
58
|
+
builder.manifest(@attributes) {
|
59
|
+
@items.each {
|
60
|
+
|itemid, item|
|
61
|
+
item.to_xml(builder, @opf_version)
|
62
|
+
}
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def register_item(item)
|
67
|
+
raise "id '#{item.id}' is already in use." if @id_pool[item.id]
|
68
|
+
@id_pool[item.id] = true
|
69
|
+
end
|
70
|
+
|
71
|
+
def unregister_item(item)
|
72
|
+
@items[item.id] = nil
|
73
|
+
@items_by_href[item.href] = nil
|
74
|
+
@id_pool[item.id] = nil
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
data/lib/gepub/meta.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
module GEPUB
|
5
|
+
# Holds one metadata with refine meta elements.
|
6
|
+
class Meta
|
7
|
+
attr_accessor :content
|
8
|
+
attr_reader :name
|
9
|
+
def initialize(name, content, parent, attributes= {}, refiners = {})
|
10
|
+
@parent = parent
|
11
|
+
@name = name
|
12
|
+
@content = content
|
13
|
+
@attributes = attributes
|
14
|
+
@refiners = refiners
|
15
|
+
@parent.register_meta(self) unless @parent.nil?
|
16
|
+
end
|
17
|
+
|
18
|
+
def [](x)
|
19
|
+
@attributes[x]
|
20
|
+
end
|
21
|
+
|
22
|
+
def []=(x,y)
|
23
|
+
@attributes[x] = y
|
24
|
+
end
|
25
|
+
|
26
|
+
def refiner_list(name)
|
27
|
+
return @refiners[name].dup
|
28
|
+
end
|
29
|
+
|
30
|
+
def refiner_clear(name)
|
31
|
+
if !@refiners[name].nil?
|
32
|
+
@refiners[name].each {
|
33
|
+
|refiner|
|
34
|
+
@parent.unregister_meta(refiner)
|
35
|
+
}
|
36
|
+
end
|
37
|
+
@refiners[name]= []
|
38
|
+
end
|
39
|
+
|
40
|
+
def refiner(name)
|
41
|
+
refiner = @refiners[name]
|
42
|
+
if refiner.nil? || refiner.size == 0
|
43
|
+
nil
|
44
|
+
else
|
45
|
+
refiner[0]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# add a refiner.
|
50
|
+
def add_refiner(property, content, attributes = {})
|
51
|
+
(@refiners[property] ||= []) << refiner = Meta.new('meta', content, @parent, { 'property' => property }.merge(attributes)) unless content.nil?
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
# set a 'unique' refiner. all other refiners with same property will be removed.
|
56
|
+
def refine(property, content, attributes = {})
|
57
|
+
if !content.nil?
|
58
|
+
refiner_clear(property)
|
59
|
+
add_refiner(property, content, attributes)
|
60
|
+
end
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
['title-type', 'identifier-type', 'display-seq', 'file-as', 'group-position'].each {
|
65
|
+
|name|
|
66
|
+
methodbase = name.sub('-','_')
|
67
|
+
define_method(methodbase + '=') { |val| refine(name, val); }
|
68
|
+
define_method('set_' + methodbase) { |val| refine(name, val); }
|
69
|
+
define_method(methodbase) { refiner(name) }
|
70
|
+
}
|
71
|
+
|
72
|
+
# add alternate script refiner.
|
73
|
+
def add_alternates(alternates = {})
|
74
|
+
alternates.each {
|
75
|
+
|locale, content|
|
76
|
+
add_refiner('alternate-script', content, { 'xml:lang' => locale })
|
77
|
+
}
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
def to_xml(builder, id_pool, ns = nil, additional_attr = {}, opf_version = '3.0')
|
82
|
+
additional_attr ||= {}
|
83
|
+
if @refiners.size > 0 && opf_version.to_f >= 3.0
|
84
|
+
@attributes['id'] = id_pool.generate_key(:prefix => name) if @attributes['id'].nil?
|
85
|
+
end
|
86
|
+
|
87
|
+
# using eval to parametarize Namespace and content.
|
88
|
+
eval "builder#{ ns.nil? || @name == 'meta' ? '' : '[ns]'}.#{@name}(@attributes.reject{|k,v| v.nil?}.merge(additional_attr)#{@content.nil? ? '' : ', @content'})"
|
89
|
+
|
90
|
+
if @refiners.size > 0 && opf_version.to_f >= 3.0
|
91
|
+
additional_attr['refines'] = "##{@attributes['id']}"
|
92
|
+
@refiners.each {
|
93
|
+
|k, ref_list|
|
94
|
+
ref_list.each {
|
95
|
+
|ref|
|
96
|
+
ref.to_xml(builder, id_pool, nil, additional_attr)
|
97
|
+
}
|
98
|
+
}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def to_s(locale=nil)
|
103
|
+
localized = nil
|
104
|
+
if !locale.nil?
|
105
|
+
prefix = locale.sub(/^(.+?)-.*/, '\1')
|
106
|
+
regex = Regexp.new("^((" + locale.split('-').join(')?-?(') + ")?)")
|
107
|
+
candidates = @refiners['alternate-script'].select {
|
108
|
+
|refiner|
|
109
|
+
refiner['xml:lang'] =~ /^#{prefix}-?.*/
|
110
|
+
}.sort_by {
|
111
|
+
|x|
|
112
|
+
x['xml:lang'] =~ regex; $1.size
|
113
|
+
}.reverse
|
114
|
+
localized = candidates[0].content if candidates.size > 0
|
115
|
+
end
|
116
|
+
(localized || @content || super).to_s
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,230 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
module GEPUB
|
6
|
+
# metadata constants
|
7
|
+
module TITLE_TYPE
|
8
|
+
['main','subtitle', 'short', 'collection', 'edition','expanded'].each {
|
9
|
+
|type|
|
10
|
+
const_set(type.upcase, type)
|
11
|
+
}
|
12
|
+
end
|
13
|
+
# Holds data in /package/metadata
|
14
|
+
class Metadata
|
15
|
+
include XMLUtil
|
16
|
+
attr_accessor :opf_version
|
17
|
+
# parse metadata element. metadata_xml should be Nokogiri::XML::Node object.
|
18
|
+
def self.parse(metadata_xml, opf_version = '3.0', id_pool = Package::IDPool.new)
|
19
|
+
Metadata.new(opf_version, id_pool) {
|
20
|
+
|metadata|
|
21
|
+
metadata.instance_eval {
|
22
|
+
@xml = metadata_xml
|
23
|
+
@namespaces = @xml.namespaces
|
24
|
+
CONTENT_NODE_LIST.each {
|
25
|
+
|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
|
+
}
|
32
|
+
@xml.xpath("#{ns_prefix(OPF_NS)}:meta[not(@refines) and @property]", @namespaces).each {
|
33
|
+
|node|
|
34
|
+
@meta[node['property']] = create_meta(node)
|
35
|
+
}
|
36
|
+
|
37
|
+
@oldstyle_meta = parse_opf2_meta
|
38
|
+
}
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize(opf_version = '3.0',id_pool = Package::IDPool.new)
|
43
|
+
@id_pool = id_pool
|
44
|
+
@metalist = {}
|
45
|
+
@content_nodes = {}
|
46
|
+
@meta = {}
|
47
|
+
@oldstyle_meta = []
|
48
|
+
@opf_version = opf_version
|
49
|
+
@namespaces = { 'xmlns:dc' => DC_NS }
|
50
|
+
@namespaces['xmlns:opf'] = OPF_NS if @opf_version.to_f < 3.0
|
51
|
+
yield self if block_given?
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_xml(builder)
|
55
|
+
builder.metadata(@namespaces) {
|
56
|
+
@content_nodes.each {
|
57
|
+
|name, list|
|
58
|
+
list.each {
|
59
|
+
|meta|
|
60
|
+
meta.to_xml(builder, @id_pool, ns_prefix(DC_NS), nil, @opf_version)
|
61
|
+
}
|
62
|
+
}
|
63
|
+
@oldstyle_meta.each {
|
64
|
+
|node|
|
65
|
+
node.to_xml(builder, @id_pool, nil)
|
66
|
+
}
|
67
|
+
}
|
68
|
+
@xml
|
69
|
+
end
|
70
|
+
|
71
|
+
def main_title # should make it obsolete?
|
72
|
+
@content_nodes['title'][0].content
|
73
|
+
end
|
74
|
+
|
75
|
+
def oldstyle_meta
|
76
|
+
@oldstyle_meta.dup
|
77
|
+
end
|
78
|
+
|
79
|
+
def oldstyle_meta_clear
|
80
|
+
@oldstyle_meta.each {
|
81
|
+
|meta|
|
82
|
+
unregister_meta(meta)
|
83
|
+
}
|
84
|
+
@oldstyle_meta = []
|
85
|
+
end
|
86
|
+
|
87
|
+
CONTENT_NODE_LIST = ['identifier','title', 'language', 'contributor', 'creator', 'coverage', 'date','description','format ','publisher','relation','rights','source','subject','type'].each {
|
88
|
+
|node|
|
89
|
+
define_method(node + '_list') { @content_nodes[node].dup }
|
90
|
+
define_method(node + '_clear') {
|
91
|
+
if !@content_nodes[node].nil?
|
92
|
+
@content_nodes[node].each { |x| unregister_meta(x) };
|
93
|
+
@content_nodes[node] = []
|
94
|
+
end
|
95
|
+
}
|
96
|
+
|
97
|
+
#TODO: should override for 'title'. // for 'main title' not always comes first.
|
98
|
+
define_method(node) {
|
99
|
+
if !@content_nodes[node].nil? && @content_nodes[node].size > 0
|
100
|
+
@content_nodes[node][0]
|
101
|
+
end
|
102
|
+
}
|
103
|
+
|
104
|
+
define_method('add_' + node) {
|
105
|
+
|content, id|
|
106
|
+
add_metadata(node, content, id)
|
107
|
+
}
|
108
|
+
|
109
|
+
define_method('set_' + node) {
|
110
|
+
|content, id|
|
111
|
+
send(node + "_clear")
|
112
|
+
add_metadata(node, content, id)
|
113
|
+
}
|
114
|
+
|
115
|
+
define_method(node+'=') {
|
116
|
+
|content|
|
117
|
+
send(node + "_clear")
|
118
|
+
add_metadata(node, content)
|
119
|
+
}
|
120
|
+
}
|
121
|
+
|
122
|
+
def add_identifier(string, id, type=nil)
|
123
|
+
raise 'id #{id} is already in use' if @id_pool[id]
|
124
|
+
identifier = add_metadata('identifier', string, id)
|
125
|
+
identifier.refine('identifier-type', type) unless type.nil?
|
126
|
+
identifier
|
127
|
+
end
|
128
|
+
|
129
|
+
def identifier_by_id(id)
|
130
|
+
@content_nodes['identifier'].each {
|
131
|
+
|x|
|
132
|
+
return x.content if x['id'] == id
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
def add_metadata(name, content, id = nil)
|
137
|
+
meta = Meta.new(name, content, self, { 'id' => id })
|
138
|
+
(@content_nodes[name] ||= []) << meta
|
139
|
+
yield self if block_given?
|
140
|
+
meta
|
141
|
+
end
|
142
|
+
|
143
|
+
def add_title(content, id = nil, title_type = nil)
|
144
|
+
meta = add_metadata('title', content, id).refine('title-type', title_type)
|
145
|
+
yield meta if block_given?
|
146
|
+
meta
|
147
|
+
end
|
148
|
+
|
149
|
+
def add_person(name, content, id = nil, role = 'aut')
|
150
|
+
meta = add_metadata(name, content, id).refine('role', role)
|
151
|
+
yield meta if block_given?
|
152
|
+
meta
|
153
|
+
end
|
154
|
+
|
155
|
+
def add_creator(content, id = nil, role = 'aut')
|
156
|
+
meta = add_person('creator', content, id, role)
|
157
|
+
yield meta if block_given?
|
158
|
+
meta
|
159
|
+
end
|
160
|
+
|
161
|
+
def add_contributor(content, id=nil, role=nil)
|
162
|
+
meta = add_person('contributor', content, id, role)
|
163
|
+
yield meta if block_given?
|
164
|
+
meta
|
165
|
+
end
|
166
|
+
|
167
|
+
def set_lastmodified(date=nil)
|
168
|
+
date ||= Time.now
|
169
|
+
(@content_nodes['meta'] ||= []).each {
|
170
|
+
|meta|
|
171
|
+
if (meta['property'] == 'dcterms:modified')
|
172
|
+
@content_nodes['meta'].delete meta
|
173
|
+
end
|
174
|
+
}
|
175
|
+
add_metadata('meta', date.utc.strftime('%Y-%m-%dT%H:%M:%SZ'))['property'] = 'dcterms:modified'
|
176
|
+
end
|
177
|
+
|
178
|
+
def add_oldstyle_meta(content, attributes = {})
|
179
|
+
meta = Meta.new('meta', content, self, attributes)
|
180
|
+
(@oldstyle_meta ||= []) << meta
|
181
|
+
meta
|
182
|
+
end
|
183
|
+
|
184
|
+
|
185
|
+
def register_meta(meta)
|
186
|
+
if !meta['id'].nil?
|
187
|
+
raise "id '#{meta['id']}' is already in use." if @id_pool[meta['id']]
|
188
|
+
@metalist[meta['id']] = meta
|
189
|
+
@id_pool[meta['id']] = true
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def unregister_meta(meta)
|
194
|
+
if meta['id'].nil?
|
195
|
+
@metalist[meta['id']] = nil
|
196
|
+
@id_pool[meta['id']] = nil
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
private
|
201
|
+
def parse_node(ns, node)
|
202
|
+
@xml.xpath("#{ns_prefix(ns)}:#{node}", @namespaces).map {
|
203
|
+
|node|
|
204
|
+
create_meta(node)
|
205
|
+
}
|
206
|
+
end
|
207
|
+
|
208
|
+
def create_meta(node)
|
209
|
+
Meta.new(node.name, node.content, self, attr_to_hash(node.attributes), collect_refiners(node['id']))
|
210
|
+
end
|
211
|
+
|
212
|
+
def collect_refiners(id)
|
213
|
+
r = {}
|
214
|
+
if !id.nil?
|
215
|
+
@xml.xpath("//#{ns_prefix(OPF_NS)}:meta[@refines='##{id}']", @namespaces).each {
|
216
|
+
|node|
|
217
|
+
(r[node['property']] ||= []) << create_meta(node)
|
218
|
+
}
|
219
|
+
end
|
220
|
+
r
|
221
|
+
end
|
222
|
+
|
223
|
+
def parse_opf2_meta
|
224
|
+
@xml.xpath("#{ns_prefix(OPF_NS)}:meta[not(@refines) and not(@property)]").map {
|
225
|
+
|node|
|
226
|
+
create_meta(node)
|
227
|
+
}
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|