Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lib/gepub/bindings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,25 @@ class Bindings

class MediaType
attr_accessor :handler, :media_type
# @rbs (String, String) -> void
def initialize(handler, media_type)
@handler = handler
@media_type = media_type
end
# @rbs (Nokogiri::XML::Builder) -> Nokogiri::XML::Builder::NodeBuilder
def to_xml(builder)
builder.mediaType({'handler' => @handler, 'media-type' => @media_type})
end
end

# @rbs () -> void
def initialize
@media_types = []
@handler_by_media_type = {}
yield self if block_given?
end

# @rbs (Nokogiri::XML::Element?) -> GEPUB::Bindings
def self.parse(bindings_xml)
Bindings.new {
|bindings|
Expand All @@ -40,19 +44,23 @@ def self.parse(bindings_xml)
}
end

# @rbs () -> Array[untyped]
def media_types
return @media_types.dup
end

# @rbs () -> Hash[untyped, untyped]
def handler_by_media_type
return @handler_by_media_type.dup
end

# @rbs (String, String) -> void
def add(id, media_type)
@media_types << MediaType.new(id, media_type)
@handler_by_media_type[media_type] = id
end

# @rbs (Nokogiri::XML::Builder) -> Nokogiri::XML::Builder::NodeBuilder?
def to_xml(builder)
if (media_types.size > 0)
builder.bindings {
Expand Down
26 changes: 26 additions & 0 deletions lib/gepub/book.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class Book
ROOTFILE_PATTERN=/^.+\.opf$/
CONTAINER_NS='urn:oasis:names:tc:opendocument:xmlns:container'

# @rbs (String) -> String
def self.rootfile_from_container(rootfile)
doc = Nokogiri::XML::Document.parse(rootfile)
ns = doc.root.namespaces
Expand All @@ -97,6 +98,7 @@ def self.rootfile_from_container(rootfile)
# Parses existing EPUB2/EPUB3 files from an IO object or a file path and creates new Book object.
# book = self.parse(File.new('some.epub'))

# @rbs (File | String) -> GEPUB::Book
def self.parse(path_or_io)
files = {}
package = nil
Expand All @@ -115,6 +117,7 @@ def self.parse(path_or_io)

# creates new empty Book object.
# usually you do not need to specify any arguments.
# @rbs (?String, ?Hash[untyped, untyped]) -> void
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='
Expand All @@ -129,11 +132,13 @@ def initialize(path='OEBPS/package.opf', attributes = {}, &block)


# Get optional(not required in EPUB specification) files in the container.
# @rbs () -> Hash[untyped, untyped]
def optional_files
@optional_files || {}
end

# Add an optional file to the container
# @rbs (String, StringIO) -> String
def add_optional_file(path, io_or_filename)
io = io_or_filename
if io_or_filename.class == String
Expand All @@ -143,6 +148,7 @@ def add_optional_file(path, io_or_filename)
(@optional_files ||= {})[path] = io.read
end

# @rbs (GEPUB::Item) -> void
def set_singleton_methods_to_item(item)
toc = @toc
metaclass = (class << item;self;end)
Expand All @@ -162,28 +168,33 @@ def set_singleton_methods_to_item(item)


# get handler item which defined in bindings for media type,
# @rbs (String) -> GEPUB::Item
def get_handler_of(media_type)
items[@package.bindings.handler_by_media_type[media_type]]
end

# @rbs (Symbol, *nil | String | String? | String | Hash[untyped, untyped] | Time | Symbol, **untyped) -> (String | GEPUB::Meta | Array[untyped] | GEPUB::DateMeta | Hash[untyped, untyped] | GEPUB::Item | GEPUB::Spine)?
ruby2_keywords def method_missing(name, *args, &block)
@package.send(name, *args, &block)
end

# should call ordered() with block.
# within the block, all item added by add_item will be added to spine also.
# @rbs () -> nil
def ordered(&block)
@package.ordered(&block)
end

# clenup and maintain consistency of metadata and items included in the Book
# object.
# @rbs () -> void
def cleanup
cleanup_for_epub2
cleanup_for_epub3
end

# write EPUB to stream specified by the argument.
# @rbs (Zip::OutputStream) -> Array[untyped]
def write_to_epub_container(epub)
mod_time = Zip::DOSTime.now
unless (last_mod = lastmodified).nil?
Expand Down Expand Up @@ -219,6 +230,7 @@ def write_to_epub_container(epub)
end

# generates and returns StringIO contains EPUB.
# @rbs () -> StringIO
def generate_epub_stream
cleanup
Zip::OutputStream::write_buffer(StringIO.new) do
Expand All @@ -228,6 +240,7 @@ def generate_epub_stream
end

# writes EPUB to file. if file exists, it will be overwritten.
# @rbs (Pathname) -> Array[untyped]
def generate_epub(path_to_epub)
cleanup
File.delete(path_to_epub) if File.exist?(path_to_epub)
Expand All @@ -237,6 +250,7 @@ def generate_epub(path_to_epub)
}
end

# @rbs () -> String
def container_xml
<<EOF
<?xml version="1.0" encoding="UTF-8"?>
Expand All @@ -262,10 +276,12 @@ def add_tocdata(toc_yaml)
@toc = @toc + newtoc
end

# @rbs (?String) -> GEPUB::Item
def generate_nav_doc(title = 'Table of Contents')
add_item('nav.xhtml', id: 'nav', content: StringIO.new(nav_doc(title))).add_property('nav')
end

# @rbs (?String) -> String
def nav_doc(title = 'Table of Contents')
# handle cascaded toc
start_level = @toc && !@toc.empty? && @toc[0][:level] || 1
Expand All @@ -286,6 +302,7 @@ def nav_doc(title = 'Table of Contents')
current_stack
end
# write toc
# @rbs (Nokogiri::XML::Builder, Array[untyped]) -> Nokogiri::XML::Builder::NodeBuilder?
def write_toc xml_doc, tocs
return if tocs.empty?
xml_doc.ol {
Expand All @@ -303,6 +320,7 @@ def write_toc xml_doc, tocs
}
}
end
# @rbs (Nokogiri::XML::Builder, Array[untyped]) -> Nokogiri::XML::Builder::NodeBuilder
def write_landmarks xml_doc, landmarks
xml_doc.ol {
landmarks.each {
Expand Down Expand Up @@ -343,6 +361,7 @@ def write_landmarks xml_doc, landmarks
builder.to_xml(:encoding => 'utf-8')
end

# @rbs () -> String
def ncx_xml
builder = Nokogiri::XML::Builder.new {
|xml|
Expand Down Expand Up @@ -379,6 +398,7 @@ def ncx_xml
end

private
# @rbs (Zip::File, Hash[untyped, untyped]) -> Array[untyped]
def self.parse_container(zip_file, files)
package_path = nil
package = nil
Expand All @@ -404,6 +424,7 @@ def self.parse_container(zip_file, files)
end
private_class_method :parse_container

# @rbs (GEPUB::Package, String) -> void
def self.check_consistency_of_package(package, package_path)
if package.nil?
raise 'this container do not cotains publication information file'
Expand All @@ -415,6 +436,7 @@ def self.check_consistency_of_package(package, package_path)
end
private_class_method :check_consistency_of_package

# @rbs (Hash[untyped, untyped], GEPUB::Package) -> void
def self.parse_files_into_package(files, package)
files.each {
|k, content|
Expand All @@ -427,6 +449,7 @@ def self.parse_files_into_package(files, package)
end
private_class_method :parse_files_into_package

# @rbs () -> void
def cleanup_for_epub2
if version.to_f < 3.0 || @package.epub_backward_compat
if @package.manifest.item_list.select {
Expand All @@ -440,6 +463,7 @@ def cleanup_for_epub2
end
end
end
# @rbs () -> Array[untyped]?
def cleanup_for_epub3
if version.to_f >=3.0
@package.metadata.modified_now unless @package.metadata.lastmodified_updated?
Expand All @@ -460,6 +484,7 @@ def cleanup_for_epub3

private

# @rbs (String, item_attributes: Hash[untyped, untyped], ordered: bool, ?content: nil | String | File | StringIO, ?attributes: Hash[untyped, untyped]) -> GEPUB::Item
def add_item_internal(href, content: nil, item_attributes: , attributes: {}, ordered: )
id = item_attributes.delete(:id)
item =
Expand All @@ -483,6 +508,7 @@ def add_item_internal(href, content: nil, item_attributes: , attributes: {}, ord
item
end

# @rbs (StringIO?, String?, nil, (String | File | StringIO)?, String?, Hash[untyped, untyped]) -> Array[untyped]
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'
Expand Down
2 changes: 2 additions & 0 deletions lib/gepub/book_add_item.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module GEPUB
class Book
# 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.
# @rbs (String, ?nil, ?String?, ?nil, ?content: nil | String | File | StringIO, ?id: nil | String, ?media_type: String | nil, ?fallback: nil | String, ?properties: nil | Array[untyped], ?media_overlay: nil | String, ?toc_text: nil | String, ?property: nil | String, ?attributes: Hash[untyped, untyped]) -> GEPUB::Item
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: {})
Expand All @@ -12,6 +13,7 @@ def add_item(href, deprecated_content = nil, deprecated_id = nil, deprecated_att
end

# same as add_item, but the item will be added to spine of the EPUB.
# @rbs (String, ?StringIO?, ?String?, ?nil, ?content: nil | StringIO, ?id: nil | String, ?media_type: String | nil, ?fallback: nil | String, ?properties: nil | Array[untyped], ?media_overlay: nil | String, ?toc_text: nil | String, ?property: nil | String, ?attributes: Hash[untyped, untyped]) -> GEPUB::Item
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: {})
Expand Down
18 changes: 18 additions & 0 deletions lib/gepub/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -152,18 +152,22 @@ module GEPUB
class Builder
include BuilderMixin
class MetaItem
# @rbs (GEPUB::Meta) -> void
def initialize(item)
@item = item
end

# @rbs () -> bool
def apply_one_to_multi
false
end

# @rbs (?Hash[untyped, untyped]) -> GEPUB::Meta
def alt(alternates = {})
@item.add_alternates(alternates)
end

# @rbs (String) -> GEPUB::Meta
def file_as(name)
@item.file_as(name)
end
Expand All @@ -181,6 +185,7 @@ def id(val)
end
end

# @rbs (?Hash[untyped, untyped]) -> void
def initialize(_attributes = {}, &block)
@last_defined_item = nil
@book = Book.new
Expand Down Expand Up @@ -214,15 +219,18 @@ def initialize(_attributes = {}, &block)
end
}

# @rbs (String, ?Integer) -> GEPUB::Builder::MetaItem
def collection(val, count = 1)
@last_defined_item =
MetaItem.new(@book.add_title(val, title_type: GEPUB::TITLE_TYPE::COLLECTION).group_position(count.to_s))
end

# @rbs (String, ?String) -> GEPUB::Builder::MetaItem
def creator(val, role = 'aut')
MetaItem.new(@book.add_creator(val, role: role))
end

# @rbs (*String | Array[untyped]) -> Array[untyped]
def creators(*vals)
@last_defined_item = vals.map {
|v|
Expand All @@ -233,6 +241,7 @@ def creators(*vals)
}
end

# @rbs (*String) -> void
def contributors(*vals)
@last_defined_item = vals.map {
|v|
Expand All @@ -250,10 +259,12 @@ def publishers(*vals)
}
end

# @rbs (String, ?String, ?String) -> GEPUB::Builder::MetaItem
def unique_identifier(val, id = 'BookID', scheme = 'nil')
@last_defined_item = MetaItem.new(@book.primary_identifier(val, id, scheme))
end

# @rbs (?Hash[untyped, untyped]) -> Array[untyped]
def alts(alt_vals = {})
raise "can't specify alts on single item" if ! Array === @last_defined_item
@last_defined_item.each_with_index {
Expand All @@ -262,6 +273,7 @@ def alts(alt_vals = {})
}
end

# @rbs (String, ?String) -> GEPUB::Builder::MetaItem
def contributor(val, role = nil)
MetaItem.new(@book.add_contributor(val, role: role))
end
Expand All @@ -272,10 +284,12 @@ def page_progression_direction(val)
@book.page_progression_direction = val
end
# specify version for ibooks
# @rbs (String) -> void
def ibooks_version(val)
@book.ibooks_version=val
end
# specify scroll axis for ibooks
# @rbs (Symbol) -> void
def ibooks_scroll_axis(val)
@book.ibooks_scroll_axis = val
end
Expand All @@ -284,6 +298,7 @@ def ibooks_scroll_axis(val)
# val should be String or Hash.
# if val is String, file is read from the File specified by string and stored in EPUB to the path specified by string.
# if val is Hash, file is read from the value and stored in EPUB to the path specified by the key.
# @rbs (Hash[untyped, untyped]) -> String
def optional_file(val)
path = val
io = val if String === val
Expand All @@ -295,16 +310,19 @@ def optional_file(val)
@book.add_optional_file(path, io)
end

# @rbs (Pathname) -> void
def generate_epub(path_to_epub)
@book.generate_epub(path_to_epub)
end

# @rbs (?Hash[untyped, untyped]) -> GEPUB::ResourceBuilder
def resources(attributes = {}, &block)
ResourceBuilder.new(@book, attributes, &block)
end
def book
@book
end
# @rbs () -> StringIO
def generate_epub_stream
@book.generate_epub_stream
end
Expand Down
1 change: 1 addition & 0 deletions lib/gepub/builder_mixin.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module GEPUB
module BuilderMixin
# @rbs (Symbol, *Hash[untyped, untyped] | String) -> (GEPUB::Meta | Array[untyped] | GEPUB::Item)
def method_missing(name, *args, &block)
if Array === @last_defined_item &&
@last_defined_item.size > 0 &&
Expand Down
Loading