class GEPUB::Book
Book
is the class to hold data in EPUB files.
It can generate and parse EPUB2/EPUB3 files.
Book
delegates many methods to objects in other class, so you can't find them in Book#methods or in ri/rdoc documentation. Their descriptions are below.
Package Attributes¶ ↑
Book#version (delegated to Package#version
)¶ ↑
returns OPF version.
Book#version=, Book#set_version (delegated to Package#version=
)¶ ↑
set OPF version
Book#unique_identifier (delegated to Package#unique_identifier)¶ ↑
return unique_identifier ID value. identifier itself can be get by Book#identifier
Metadata¶ ↑
Metadata items(e.g. title, creator, publisher, etc) are GEPUB::Meta
objects.
Book#identifier (delegated to Package#identifier
)¶ ↑
return GEPUB::Meta
object of unique identifier.
Book#identifier=(identifier) (delegated to Package#identifier=
)¶ ↑
set identifier (i.e. url, uuid, ISBN) as unique-identifier of EPUB.
Book#set_main_id(identifier, id = nil, type = nil) (delegated to Package#set_main_id)¶ ↑
same as identifier=, but can specify id (in the opf xml) and identifier type(i.e. URL, uuid, ISBN, etc)
Book#add_identifier(string, id, type=nil) (delegated to Metadata#add_identifier
)¶ ↑
Set an identifier metadata. It it not unique-identifier in opf. Many EPUB files do not set identifier other than unique-identifier.
Book#add_title(content, id: nil, title_type: nil) (delegated to Metadata#add_title
)¶ ↑
add title metadata. title_type candidates is defined in TITLE_TYPES.
Book#title(content, id = nil, title_type = nil) (delegated to Metadata#title
)¶ ↑
clear all titles and then add title.
Book#title (delegated to Metadata
)¶ ↑
returns 'main' title Meta
object. 'main' title is determined by this order:
-
title-type is 'main'
-
display-seq is smallest
-
appears first in opf file
Book#title_list (delegated to Metadata
)¶ ↑
returns titles list by display-seq or defined order. the title without display-seq is appear after titles with display-seq.
Book#add_creator(content, id = nil, role = 'aut') (delegated to Metadata#add_creator
)¶ ↑
add creator.
Book#creator¶ ↑
returns 'main' creator Meta
object. 'main' creator is determined as following:
-
display-seq is smallest
-
appears first in opf file
Book#creator_list (delegated to Metadata
)¶ ↑
returns creators list by display-seq or defined order. the creators without display-seq is appear after creators with display-seq.
Book#add_contributor(content, id = nil, role = 'aut') (delegated to Metadata#add_contributor
)¶ ↑
add contributor.
Book#contributor(content, id = nil, role = 'aut') (delegated to Metadata#contributor)¶ ↑
returns 'main' contributor. 'main' contributor determined as following:
-
display-seq is smallest
-
appears first in opf file
Book#contributors_list (delegated to Metadata
)¶ ↑
returns contributors list by display-seq or defined order. the contributors without display-seq is appear after contributors with display-seq.
Book#lastmodified(date) (delegated to Metadata#lastmodified
)¶ ↑
set last modified date. date is a Time, DateTime or string that can be parsed by DateTime#parse.
Book#modified_now (delegated to Metadata#modified_now
)¶ ↑
set last modified date to current time.
Book#lastmodified (delegated to Metadata#lastmodified
)¶ ↑
returns Meta
object contains last modified time.
setting and reading other metadata: publisher, language, coverage, date, description, format, relation, rights, source, subject, type (delegated to Metadata
)¶ ↑
they all have methods like: publisher(which returns 'main' publisher), add_publisher(content, id) (which add publisher), publisher= (clears and set publisher), and publisher_list(returns publisher Meta
object in display-seq order).
Book#page_progression_direction= (delegated to Spine#page_progression_direction=)¶ ↑
set page-proression-direction attribute to spine.
Constants
- CONTAINER
- CONTAINER_NS
- MIMETYPE
- MIMETYPE_CONTENTS
- ROOTFILE_PATTERN
Public Class Methods
creates new empty Book
object. usually you do not need to specify any arguments.
# File lib/gepub/book.rb, line 115 def initialize(path='OEBPS/package.opf', attributes = {}, &block) if File.extname(path) != '.opf' warn 'GEPUB::Book#new interface changed. You must supply path to package.opf as first argument. If you want to set title, please use GEPUB::Book#title=' end @package = Package.new(path, attributes) @toc = [] @landmarks = [] if block block.arity < 1 ? instance_eval(&block) : block[self] end end
Parses existing EPUB2/EPUB3 files from an IO object, and creates new Book
object.
book = self.parse(File.new('some.epub'))
# File lib/gepub/book.rb, line 97 def self.parse(io) files = {} package = nil package_path = nil book = nil Zip::InputStream::open(io) { |zis| package, package_path = parse_container(zis, files) check_consistency_of_package(package, package_path) parse_files_into_package(files, package) book = Book.new(package.path) book.instance_eval { @package = package; @optional_files = files } } book end
# File lib/gepub/book.rb, line 87 def self.rootfile_from_container(rootfile) doc = Nokogiri::XML::Document.parse(rootfile) ns = doc.root.namespaces defaultns = ns.select{ |_name, value| value == CONTAINER_NS }.to_a[0][0] doc.css("#{defaultns}|rootfiles > #{defaultns}|rootfile")[0]['full-path'] end
Private Class Methods
# File lib/gepub/book.rb, line 404 def self.check_consistency_of_package(package, package_path) if package.nil? raise 'this container do not cotains publication information file' end if package_path != package.path warn "inconsistend EPUB file: container says opf is #{package_path}, but actually #{package.path}" end end
# File lib/gepub/book.rb, line 379 def self.parse_container(zis, files) package_path = nil package = nil while entry = zis.get_next_entry if !entry.directory? files[entry.name] = zis.read case entry.name when MIMETYPE then if files[MIMETYPE] != MIMETYPE_CONTENTS warn "#{MIMETYPE} is not valid: should be #{MIMETYPE_CONTENTS} but was #{files[MIMETYPE]}" end files.delete(MIMETYPE) when CONTAINER then package_path = rootfile_from_container(files[CONTAINER]) files.delete(CONTAINER) when ROOTFILE_PATTERN then package = Package.parse_opf(files[entry.name], entry.name) files.delete(entry.name) end end end return package, package_path end
# File lib/gepub/book.rb, line 415 def self.parse_files_into_package(files, package) files.each { |k, content| item = package.manifest.item_by_href(k.sub(/^#{package.contents_prefix}/,'')) if !item.nil? files.delete(k) item.add_raw_content(content) end } end
Public Instance Methods
add an item(i.e. html, images, audios, etc) to Book
. the added item will be referenced by the first argument in the EPUB container.
# File lib/gepub/book_add_item.rb, line 7 def add_item(href, deprecated_content = nil, deprecated_id = nil, deprecated_attributes = nil, content: nil, id: nil,media_type: nil,fallback: nil,properties: nil,media_overlay: nil,toc_text: nil,property: nil, attributes: {}) content, id, attributes = handle_deprecated_add_item_arguments(deprecated_content, deprecated_id, deprecated_attributes, content, id, attributes) add_item_internal(href, content: content, item_attributes: { id: id,media_type: media_type,fallback: fallback,properties: properties,media_overlay: media_overlay,toc_text: toc_text,property: property }, attributes: attributes, ordered: false) end
Add an optional file to the container
# File lib/gepub/book.rb, line 134 def add_optional_file(path, io_or_filename) io = io_or_filename if io_or_filename.class == String io = File.new(io_or_filename) end io.binmode (@optional_files ||= {})[path] = io.read end
same as add_item
, but the item will be added to spine of the EPUB.
# File lib/gepub/book_add_item.rb, line 15 def add_ordered_item(href, deprecated_content = nil, deprecated_id = nil, deprecated_attributes = nil, content:nil, id: nil,media_type: nil,fallback: nil,properties: nil,media_overlay: nil,toc_text: nil,property: nil, attributes: {}) content, id, attributes = handle_deprecated_add_item_arguments(deprecated_content, deprecated_id, deprecated_attributes, content, id, attributes) add_item_internal(href, content: content, item_attributes: { id: id,media_type: media_type,fallback: fallback,properties: properties,media_overlay: media_overlay,toc_text: toc_text,property: property }, attributes: attributes, ordered: true) end
add tocdata like this : [ {link: chapter1.xhtml, text: 'Capter 1', level: 1} ] . if item corresponding to the link does not exists, error will be thrown.
# File lib/gepub/book.rb, line 251 def add_tocdata(toc_yaml) newtoc = [] toc_yaml.each do |toc_entry| href, id = toc_entry[:link].split('#') item = @package.manifest.item_by_href(href) throw "#{href} does not exist." if item.nil? newtoc.push({item: item, id: id, text: toc_entry[:text], level: toc_entry[:level] }) end @toc = @toc + newtoc end
clenup and maintain consistency of metadata and items included in the Book
object.
# File lib/gepub/book.rb, line 178 def cleanup cleanup_for_epub2 cleanup_for_epub3 end
# File lib/gepub/book.rb, line 237 def container_xml <<EOF <container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container"> <rootfiles> <rootfile full-path="#{@package.path}" media-type="application/oebps-package+xml"/> </rootfiles> </container> EOF end
writes EPUB to file. if file exists, it will be overwritten.
# File lib/gepub/book.rb, line 228 def generate_epub(path_to_epub) cleanup File.delete(path_to_epub) if File.exist?(path_to_epub) Zip::OutputStream::open(path_to_epub) { |epub| write_to_epub_container(epub) } end
generates and returns StringIO contains EPUB.
# File lib/gepub/book.rb, line 219 def generate_epub_stream cleanup Zip::OutputStream::write_buffer(StringIO.new) do |epub| write_to_epub_container(epub) end end
get handler item which defined in bindings for media type,
# File lib/gepub/book.rb, line 162 def get_handler_of(media_type) items[@package.bindings.handler_by_media_type[media_type]] end
# File lib/gepub/book.rb, line 166 def method_missing(name, *args, &block) @package.send(name, *args, &block) end
# File lib/gepub/book.rb, line 343 def ncx_xml builder = Nokogiri::XML::Builder.new { |xml| xml.ncx('xmlns' => 'http://www.daisy.org/z3986/2005/ncx/', 'version' => '2005-1') { xml.head { xml.meta('name' => 'dtb:uid', 'content' => "#{self.identifier}") xml.meta('name' => 'dtb:depth', 'content' => '1') xml.meta('name' => 'dtb:totalPageCount','content' => '0') xml.meta('name' => 'dtb:maxPageNumber', 'content' => '0') } xml.docTitle { xml.text_ "#{@package.metadata.title}" } count = 1 xml.navMap { @toc.each { |x| xml.navPoint('id' => "#{x[:item].itemid}_#{x[:id]}", 'playOrder' => "#{count}") { xml.navLabel { xml.text_ "#{x[:text]}" } if x[:id].nil? xml.content('src' => "#{x[:item].href}") else xml.content('src' => "#{x[:item].href}##{x[:id]}") end } count += 1 } } } } builder.to_xml(:encoding => 'utf-8') end
Get optional(not required in EPUB specification) files in the container.
# File lib/gepub/book.rb, line 129 def optional_files @optional_files || {} end
should call ordered() with block. within the block, all item added by add_item
will be added to spine also.
# File lib/gepub/book.rb, line 172 def ordered(&block) @package.ordered(&block) end
# File lib/gepub/book.rb, line 143 def set_singleton_methods_to_item(item) toc = @toc metaclass = (class << item;self;end) metaclass.send(:define_method, :toc, Proc.new { toc }) landmarks = @landmarks metaclass.send(:define_method, :landmarks, Proc.new { landmarks }) bindings = @package.bindings metaclass.send(:define_method, :bindings, Proc.new { bindings }) end
# File lib/gepub/book.rb, line 303 def write_landmarks xml_doc, landmarks xml_doc.ol { landmarks.each { |landmark| id = landmark[:id].nil? ? "" : "##{x[:id]}" landmark_title = landmark[:title] xml_doc.li { xml_doc.a({'href' => landmark[:item].href + id, 'epub:type' => landmark[:type]}, landmark_title) } } } end
write EPUB to stream specified by the argument.
# File lib/gepub/book.rb, line 184 def write_to_epub_container(epub) mod_time = Zip::DOSTime.now unless (last_mod = lastmodified).nil? tm = last_mod.content mod_time = Zip::DOSTime.local(tm.year, tm.month, tm.day, tm.hour, tm.min, tm.sec) end mimetype_entry = Zip::Entry.new(nil, 'mimetype', nil, nil, nil, nil, nil, nil, mod_time) epub.put_next_entry(mimetype_entry, nil, nil, Zip::Entry::STORED) epub << "application/epub+zip" entries = {} optional_files.each { |k, content| entries[k] = content } entries['META-INF/container.xml'] = container_xml entries[@package.path] = opf_xml @package.manifest.item_list.each { |_k, item| if item.content != nil entries[@package.contents_prefix + item.href] = item.content end } entries.sort_by { |k,_v| k }.each { |k,v| zip_entry = Zip::Entry.new(nil, k, nil, nil, nil, nil, nil, nil, mod_time) epub.put_next_entry(zip_entry) epub << v.force_encoding('us-ascii') } end
write toc
# File lib/gepub/book.rb, line 286 def write_toc xml_doc, tocs return if tocs.empty? xml_doc.ol { tocs.each { |x| id = x[:id].nil? ? "" : "##{x[:id]}" toc_text = x[:text] toc_text = x[:item].href if toc_text.nil? or toc_text == '' xml_doc.li { xml_doc.a({'href' => x[:item].href + id} ,toc_text) if x[:child_stack] && x[:child_stack][:tocs].size > 0 write_toc(xml_doc, x[:child_stack][:tocs]) end } } } end
Private Instance Methods
# File lib/gepub/book.rb, line 460 def add_item_internal(href, content: nil, item_attributes: , attributes: {}, ordered: ) id = item_attributes.delete(:id) item = if ordered @package.add_ordered_item(href,attributes: attributes, id:id, content: content) else @package.add_item(href, attributes: attributes, id: id, content: content) end set_singleton_methods_to_item(item) item_attributes.each do |attr, val| next if val.nil? method_name = if attr == :toc_text "" elsif attr == :property "add_" else "set_" end + attr.to_s item.send(method_name, val) end item end
# File lib/gepub/book.rb, line 427 def cleanup_for_epub2 if version.to_f < 3.0 || @package.epub_backward_compat if @package.manifest.item_list.select { |_x,item| item.media_type == 'application/x-dtbncx+xml' }.size == 0 if (@toc.size == 0 && !@package.spine.itemref_list.empty?) @toc << { :item => @package.manifest.item_list[@package.spine.itemref_list[0].idref] } end add_item('toc.ncx', id: 'ncx', content: StringIO.new(ncx_xml)) end end end
# File lib/gepub/book.rb, line 440 def cleanup_for_epub3 if version.to_f >=3.0 @package.metadata.modified_now unless @package.metadata.lastmodified_updated? if @package.manifest.item_list.select { |_href, item| (item.properties||[]).member? 'nav' }.size == 0 generate_nav_doc end @package.spine.remove_with_idlist @package.manifest.item_list.map { |_href, item| item.fallback }.reject(&:nil?) end end
# File lib/gepub/book.rb, line 483 def handle_deprecated_add_item_arguments(deprecated_content, deprecated_id, deprecated_attributes, content, id, attributes) if deprecated_content msg = 'deprecated argument; use content keyword argument instead of 2nd argument' fail msg if content warn msg content = deprecated_content end if deprecated_id msg = 'deprecated argument; use id keyword argument instead of 3rd argument' fail msg if id warn msg id = deprecated_id end if deprecated_attributes msg = 'deprecated argument; use argument keyword attributes instead of 4th argument' fail msg if attributes.size > 0 warn msg attributes = deprecated_attributes end return content, id, attributes end