lib/gepub/spine.rb
require 'rubygems'
require 'nokogiri'
module GEPUB
class Spine
include XMLUtil
include InspectMixin
attr_accessor :opf_version
class Itemref
def self.create(parent, attributes = {})
Itemref.new(attributes['idref'], parent, attributes.reject{|k,_v| k == 'idref'})
end
def initialize(idref, parent = nil, attributes = {})
if attributes['properties'].class == String
attributes['properties'] = attributes['properties'].split(' ')
end
@attributes = {'idref' => idref}.merge(attributes)
@parent = parent
@parent.register_itemref(self) unless @parent.nil?
self
end
['idref', 'linear', 'id', 'properties'].each { |name|
methodbase = name.gsub('-','_')
define_method(methodbase + '=') { |val| @attributes[name] = val }
define_method('set_' + methodbase) { |val| @attributes[name] = val }
define_method(methodbase) { @attributes[name] }
}
# get +attribute+
def [](attribute)
@attributes[attribute]
end
# set +attribute+
def []=(attribute, value)
@attributes[attribute] = value
end
def add_property(property)
(@attributes['properties'] ||=[]) << property
end
def page_spread_right
add_property 'page-spread-right'
end
def page_spread_left
add_property 'page-spread-left'
end
def rendition_specified?
@rendition_specified
end
def set_rendition_param(name, val)
add_property "rendition:#{name}-#{val}"
@rendition_specified = true
end
def rendition_layout=(val)
set_rendition_param('layout', val)
end
def rendition_orientation=(val)
set_rendition_param('orientation', val)
end
def rendition_spread=(val)
set_rendition_param('spread', val)
end
def to_xml(builder, opf_version)
attr = @attributes.dup
if opf_version.to_f < 3.0
attr.reject!{ |k,_v| k == 'properties' }
end
if !attr['properties'].nil?
attr['properties'] = attr['properties'].join(' ')
if attr['properties'].size == 0
attr.delete 'properties'
end
end
builder.itemref(attr)
end
end
def self.parse(spine_xml, opf_version = '3.0', id_pool = Package::IDPool.new)
Spine.new(opf_version, id_pool) {
|spine|
spine.instance_eval {
@xml = spine_xml
@namespaces = @xml.namespaces
@attributes = attr_to_hash(@xml.attributes)
@item_refs = []
@xml.xpath("//#{ns_prefix(OPF_NS)}:spine/#{ns_prefix(OPF_NS)}:itemref", @namespaces).map {
|itemref|
i = Itemref.create(self, attr_to_hash(itemref.attributes))
@item_refs << i
}
}
}
end
def initialize(opf_version = '3.0', id_pool = Package::IDPool.new)
@id_pool = id_pool
@attributes = {}
@item_refs = []
@itemref_by_id = {}
@opf_version = opf_version
yield self if block_given?
end
['id', 'toc', 'page-progression-direction'].each { |name|
methodbase = name.gsub('-','_')
define_method(methodbase + '=') { |val| @attributes[name] = val }
define_method('set_' + methodbase) { |val| @attributes[name] = val }
define_method(methodbase) { @attributes[name] }
}
def itemref_list
@item_refs.dup
end
def itemref_by_id
@itemref_by_id.dup
end
def push(item)
@item_refs << i = Itemref.new(item.id, self)
@itemref_by_id[item.id] = i
i
end
def <<(item)
push item
end
def rendition_specified?
@item_refs.select { |itemref| itemref.rendition_specified? }.size > 0
end
def to_xml(builder)
builder.spine(@attributes) {
@item_refs.each {
|ref|
ref.to_xml(builder, @opf_version)
}
}
end
def register_itemref(itemref)
raise "id '#{itemref.id}' is already in use." if @id_pool[itemref.id]
@id_pool[itemref.id] = true unless itemref.id.nil?
end
def unregister_itemref(itemref)
@item_refs.delete itemref
@id_pool[itemref.id] = nil
end
def remove_with_idlist(ids)
@item_refs = @item_refs.select {
|ref|
!ids.member? ref.idref
}
end
end
end