lib/gepub/builder.rb
# -*- coding: utf-8 -*-
module GEPUB
#
# Builder is a wrapper class of Book. It provides DSL to create new EPUB file.
#
# * Builder is obsolete on v0.7 and after. We will continue to support GEPUB::Builder works fine on version 1.x, but Builder will not be updated any more. For example, landmarks are not supported with Builder.
#
# = Synopsys
# # -*- coding: utf-8 -*-
# # GEPUB::Builder example.
# require 'ruby gem'
# require 'gepub'
#
# builder = GEPUB::Builder.new {
# # In the root block, you can define metadata.
# # You can define title, creator(s), contributor(s), publisher(s), date, unique_identifier, identifier, language.
# # Title can be specified with title, subtitle, collection, short_title, expanded_title, edition.
# # You can also define description, format, relation, right, source, subject, type.
# # You can 'refine' last-defined metadata by refiner/attributes methods
# # Refiner methods contains : file_as, alt
#
#
# language 'ja'
#
# title 'タイトル'
# alt 'en' => 'main title'
# file_as 'main title'
#
# subtitle 'サブタイトル'
# alt 'en' => 'subtitle'
#
# # collection title and position in the collection:
# collection 'gepub sample book series', 2
#
# #specifying creator
# creator 'author1','aut'
# alt 'ja' =>'日本語名' ,'en' =>'english name for author1'
# id 'the_first_author'
#
# #specifying multiple creator
# creators 'author1', 'author2', ['editor1', 'edt']
#
# contributor 'contributor'
# contributors 'contributor1', 'contributor2'
# # easy way to write alt {'ja' =>'日本語 for contributor1'}, {'ja' => '日本語 for contributor2'}
# alts 'ja' => ['日本語 for contributor1','日本語 for contributor2']
#
# publisher '出版社'
# alt 'en' => 'ThePublisher'
#
# date '2012-02-21T00:00:00Z'
#
# unique_identifier 'the_unique_id_in_uuid', 'uuid'
# identifier 'http://other_id','url'
# identifier 'http://another_id','url'
#
# # in resources block, you can define resources by its relative path and datasource.
# # item creator methods are: files, file.
# resources(:workdir => '~/epub_source') {
# # Reads from file. in EPUB container, they are placed at the same path.
# file 'img/image0.jpg'
# files('img/image.jpg','img/image2.jpg')
# glob 'img/*.jpg' # means files(Dir.glob('img/*.jpg'))
#
# # Reads from file. will be placed at path indicated by key.
# files('img/image.jpg' => 'imgage.jpg')
#
# # Read from IO object.
# files('img/image.png' => supplied_io, 'img/image2.png' => supplied_io2)
#
# # this will be end in error:
# # files(io1, io2)
#
# # specify remote resource.
# # only referenced from the EPUB package.
# file 'http://example.com/video/remote_video.qt'
# media_type('video/quicktime')
#
# # specify media type.
# file 'resources/pv.mp4'
# media_type('video/mp4')
#
# files('audio/voice1.mp4','audio/music1.mp4')
# media_type('audio/mp4') # applied to all items in the line above.
#
# # media_type to some file
# with_media_type('video/mp4') {
# file 'resources/v1.mp4'
# file 'resources/v2.mp4'
# file 'resources/v3.mp4'
# }
#
# # with_media_type and media_type
# with_media_type('video/mp4') {
# file 'resources/v1.mp4'
# file 'resources/v2.mp4'
# file 'resources/a4.mp4'
# media_type 'audio/mp4' # override with_media_type
# }
#
# # Read from IO object: loop
# # supplied_IOs = { 'path' => io, 'path' => io... }
# supplied_IOs.each {
# |name, io|
# file name => io
# }
#
# file 'css/default.css'
#
# # indicate property.
# # this is cover image.
# cover_image 'img/cover.jpg'
#
# # this is navigation document.
# nav 'text/toc.xhtml'
#
# # ordered item. will be added to spine.
# ordered {
# # specify texts on table of contents for auto-generated toc.
# # (if you supply navigation document with method 'nav', 'heading' has no effect.)
# file('text/chap1.xhtml')
# heading 'Chapter 1'
# file 'text/chap2.xhtml'
#
# # fallback chain: style 1
# fallback_group {
# file 'chap3_docbook.xhtml'
# mimetype('application/docbook+xml')
# file 'chap3.xml'
# mimetype "application/z3986-auth+xml"
# file 'chap3.xhtml'
# }
#
# # fallback chain: style 2
# fallback_chain_files 'chap4_docbook.xhtml', 'chap4.xml', 'chap4.xhtml'
# mimetype('application/docbook+xml','application/z3986-auth+xml' 'application/xhtml+xml')
#
# # fallback chain: style 3 + with_mimetype
# with_mimetype('application/docbook+xml','application/z3986-auth+xml' 'application/xhtml+xml') {
# fallback_chain_files 'chap5_docbook.xhtml', 'chap5.xml', 'chap5.xhtml'
# fallback_chain_files 'chap6_docbook.xhtml', 'chap6.xml', 'chap6.xhtml'
# fallback_chain_files 'chap7_docbook.xhtml', 'chap7.xml', 'chap7.xhtml'
# }
#
# }
# }
# }
#
# builder.generate_epub('sample.epub')
class Builder
include BuilderMixin
class MetaItem
def initialize(item)
@item = item
end
def apply_one_to_multi
false
end
def alt(alternates = {})
@item.add_alternates(alternates)
end
def file_as(name)
@item.file_as(name)
end
def seq(num)
@item.display_seq(num)
end
def group_position(num)
@item.group_position(num)
end
def id(val)
@item['id'] = val
end
end
def initialize(_attributes = {}, &block)
@last_defined_item = nil
@book = Book.new
instance_eval(&block)
# TODO check @book's consistency
true
end
# define base methods.
GEPUB::Metadata::CONTENT_NODE_LIST.each {
|name|
if !["title", "creator", "contributor"].include?(name)
define_method(name) { |val| @last_defined_item = MetaItem.new(@book.send("add_#{name}".to_sym, val, nil)) }
end
}
GEPUB::TITLE_TYPE::TYPES.each {
|type|
case type
when 'main'
methodname = 'title'
when 'short'
methodname = 'short_title'
when 'expanded'
methodname = 'expanded_title'
else
methodname = type
end
if methodname != "collection"
define_method(methodname) { |val| @last_defined_item = MetaItem.new(@book.add_title(val, title_type: type)) }
end
}
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
def creator(val, role = 'aut')
MetaItem.new(@book.add_creator(val, role: role))
end
def creators(*vals)
@last_defined_item = vals.map {
|v|
name = v
role = 'aut'
name,role = v[0], v[1] if Array === name
MetaItem.new(@book.add_creator(name, role: role))
}
end
def contributors(*vals)
@last_defined_item = vals.map {
|v|
name = v
role = nil
name,role = v[0], v[1] if Array === name
MetaItem.new(@book.add_contributor(name, role: role))
}
end
def publishers(*vals)
@last_defined_item = vals.map {
|v|
MetaItem.new(@book.add_publisher(v))
}
end
def unique_identifier(val, id = 'BookID', scheme = 'nil')
@last_defined_item = MetaItem.new(@book.primary_identifier(val, id, scheme))
end
def alts(alt_vals = {})
raise "can't specify alts on single item" if ! Array === @last_defined_item
@last_defined_item.each_with_index {
|item, index|
item.alt Hash[*(alt_vals.map{|k,v| [k,v[index]]}.flatten)]
}
end
def contributor(val, role = nil)
MetaItem.new(@book.add_contributor(val, role: role))
end
# set page progression direction.
def page_progression_direction(val)
raise assert unless ['rtl', 'ltr', 'default'].member? val
@book.page_progression_direction = val
end
# specify version for ibooks
def ibooks_version(val)
@book.ibooks_version=val
end
# specify scroll axis for ibooks
def ibooks_scroll_axis(val)
@book.ibooks_scroll_axis = val
end
# set optional file.
# 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.
def optional_file(val)
path = val
io = val if String === val
if Hash === val
raise 'argument to optional_file should be length 1' if val.size != 1
path = val.first[0]
io = val.first[1]
end
@book.add_optional_file(path, io)
end
def generate_epub(path_to_epub)
@book.generate_epub(path_to_epub)
end
def resources(attributes = {}, &block)
ResourceBuilder.new(@book, attributes, &block)
end
def book
@book
end
def generate_epub_stream
@book.generate_epub_stream
end
end
end