class ReVIEW::EPUBMaker

Public Class Methods

new() click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 32
def initialize
  @producer = nil
  @htmltoc = nil
  @buildlogtxt = "build-log.txt"
end

Public Instance Methods

build_body(basetmpdir, yamlfile) click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 221
def build_body(basetmpdir, yamlfile)
  @precount = 0
  @bodycount = 0
  @postcount = 0

  @manifeststr = ""
  @ncxstr = ""
  @tocdesc = Array.new

  basedir = File.dirname(yamlfile)
  base_path = Pathname.new(basedir)
  book = ReVIEW::Book.load(basedir)
  book.config = @params
  @converter = ReVIEW::Converter.new(book, ReVIEW::HTMLBuilder.new)
  @compile_errors = nil
  book.parts.each do |part|
    htmlfile = nil
    if part.name.present?
      if part.file?
        build_chap(part, base_path, basetmpdir, true)
      else
        htmlfile = "part_#{part.number}.#{@params["htmlext"]}"
        build_part(part, basetmpdir, htmlfile)
        title = ReVIEW::I18n.t("part", part.number)
        title += ReVIEW::I18n.t("chapter_postfix") + part.name.strip if part.name.strip.present?
        @htmltoc.add_item(0, htmlfile, title, {:chaptype => "part"})
        write_buildlogtxt(basetmpdir, htmlfile, "")
      end
    end

    part.chapters.each do |chap|
      build_chap(chap, base_path, basetmpdir, false)
    end

  end
  check_compile_status()
end
build_chap(chap, base_path, basetmpdir, ispart) click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 286
def build_chap(chap, base_path, basetmpdir, ispart)
  filename = ""

  chaptype = "body"
  if ispart
    chaptype = "part"
  elsif chap.on_PREDEF?
    chaptype = "pre"
  elsif chap.on_APPENDIX?
    chaptype = "post"
  end

  if ispart.present?
    filename = chap.path
  else
    filename = Pathname.new(chap.path).relative_path_from(base_path).to_s
  end
  id = filename.sub(/\.re\Z/, "")

  if @params["epubmaker"]["rename_for_legacy"] && ispart.nil?
    if chap.on_PREDEF?
      @precount += 1
      id = sprintf("pre%02d", @precount)
    elsif chap.on_APPENDIX?
      @postcount += 1
      id = sprintf("post%02d", @postcount)
    else
      @bodycount += 1
      id = sprintf("chap%02d", @bodycount)
    end
  end

  htmlfile = "#{id}.#{@params["htmlext"]}"
  write_buildlogtxt(basetmpdir, htmlfile, filename)
  log("Create #{htmlfile} from #{filename}.")

  if @params["params"].present?
    warn "'params:' in config.yml is obsoleted."
    if @params["params"] =~ /stylesheet=/
      warn "stylesheets should be defined in 'stylesheet:', not in 'params:'"
    end
  end
  begin
    @converter.convert(filename, File.join(basetmpdir, htmlfile))
    write_info_body(basetmpdir, id, htmlfile, ispart, chaptype)
    remove_hidden_title(basetmpdir, htmlfile)
  rescue => e
    @compile_errors = true
    warn "compile error in #{filename} (#{e.class})"
    warn e.message
  end
end
build_part(part, basetmpdir, htmlfile) click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 259
def build_part(part, basetmpdir, htmlfile)
  log("Create #{htmlfile} from a template.")
  File.open("#{basetmpdir}/#{htmlfile}", "w") do |f|
    @body = ""
    @body << "<div class=\"part\">\n"
    @body << "<h1 class=\"part-number\">#{CGI.escapeHTML(ReVIEW::I18n.t("part", part.number))}</h1>\n"
    if part.name.strip.present?
      @body << "<h2 class=\"part-title\">#{CGI.escapeHTML(part.name.strip)}</h2>\n"
    end
    @body << "</div>\n"

    @language = @producer.params['language']
    @stylesheets = @producer.params["stylesheet"]
    tmplfile = File.expand_path(template_name, ReVIEW::Template::TEMPLATE_DIR)
    tmpl = ReVIEW::Template.load(tmplfile)
    f.write tmpl.result(binding)
  end
end
build_path() click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 51
def build_path
  if @params["debug"]
    path = File.expand_path("#{@params["bookname"]}-epub", Dir.pwd)
    if File.exist?(path)
      FileUtils.rm_rf(path, :secure => true)
    end
    Dir.mkdir(path)
    return path
  else
    return Dir.mktmpdir("#{@params["bookname"]}-epub-")
  end
end
build_titlepage(basetmpdir, htmlfile) click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 436
def build_titlepage(basetmpdir, htmlfile)
  # TODO: should be created via epubcommon
  @title = CGI.escapeHTML(@params.name_of("booktitle"))
  File.open("#{basetmpdir}/#{htmlfile}", "w") do |f|
    @body = ""
    @body << "<div class=\"titlepage\">\n"
    @body << "<h1 class=\"tp-title\">#{CGI.escapeHTML(@params.name_of("booktitle"))}</h1>\n"
    if @params["aut"]
      @body << "<h2 class=\"tp-author\">#{CGI.escapeHTML(@params.names_of("aut").join(ReVIEW::I18n.t("names_splitter")))}</h2>\n"
    end
    if @params["prt"]
      @body << "<h3 class=\"tp-publisher\">#{CGI.escapeHTML(@params.names_of("prt").join(ReVIEW::I18n.t("names_splitter")))}</h3>\n"
    end
    @body << "</div>"

    @language = @producer.params['language']
    @stylesheets = @producer.params["stylesheet"]
    tmplfile = File.expand_path(template_name, ReVIEW::Template::TEMPLATE_DIR)
    tmpl = ReVIEW::Template.load(tmplfile)
    f.write tmpl.result(binding)
  end
end
call_hook(hook_name, *params) click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 133
def call_hook(hook_name, *params)
  filename = @params["epubmaker"][hook_name]
  log("Call #{hook_name}. (#{filename})")
  if filename.present? && File.exist?(filename) && FileTest.executable?(filename)
    if ENV["REVIEW_SAFE_MODE"].to_i & 1 > 0
      warn "hook is prohibited in safe mode. ignored."
    else
      system(filename, *params)
    end
  end
end
check_compile_status() click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 214
def check_compile_status
  return unless @compile_errors

  $stderr.puts "compile error, No EPUB file output."
  exit 1
end
copy_backmatter(basetmpdir) click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 459
def copy_backmatter(basetmpdir)
  if @params["profile"]
    FileUtils.cp(@params["profile"], "#{basetmpdir}/#{File.basename(@params["profile"])}")
    @htmltoc.add_item(1, File.basename(@params["profile"]), @producer.res.v("profiletitle"), {:chaptype => "post"})
  end

  if @params["advfile"]
    FileUtils.cp(@params["advfile"], "#{basetmpdir}/#{File.basename(@params["advfile"])}")
    @htmltoc.add_item(1, File.basename(@params["advfile"]), @producer.res.v("advtitle"), {:chaptype => "post"})
  end

  if @params["colophon"]
    if @params["colophon"].kind_of?(String) # FIXME:このやり方はやめる?
      FileUtils.cp(@params["colophon"], "#{basetmpdir}/colophon.#{@params["htmlext"]}")
    else
      File.open("#{basetmpdir}/colophon.#{@params["htmlext"]}", "w") {|f| @producer.colophon(f) }
    end
    @htmltoc.add_item(1, "colophon.#{@params["htmlext"]}", @producer.res.v("colophontitle"), {:chaptype => "post"})
  end

  if @params["backcover"]
    FileUtils.cp(@params["backcover"], "#{basetmpdir}/#{File.basename(@params["backcover"])}")
    @htmltoc.add_item(1, File.basename(@params["backcover"]), @producer.res.v("backcovertitle"), {:chaptype => "post"})
  end
end
copy_frontmatter(basetmpdir) click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 413
def copy_frontmatter(basetmpdir)
  FileUtils.cp(@params["cover"], "#{basetmpdir}/#{File.basename(@params["cover"])}") if @params["cover"].present? && File.exist?(@params["cover"])

  if @params["titlepage"]
    if @params["titlefile"].nil?
      build_titlepage(basetmpdir, "titlepage.#{@params["htmlext"]}")
    else
      FileUtils.cp(@params["titlefile"], "#{basetmpdir}/titlepage.#{@params["htmlext"]}")
    end
    @htmltoc.add_item(1, "titlepage.#{@params['htmlext']}", @producer.res.v("titlepagetitle"), {:chaptype => "pre"})
  end

  if @params["originaltitlefile"].present? && File.exist?(@params["originaltitlefile"])
    FileUtils.cp(@params["originaltitlefile"], "#{basetmpdir}/#{File.basename(@params["originaltitlefile"])}")
    @htmltoc.add_item(1, File.basename(@params["originaltitlefile"]), @producer.res.v("originaltitle"), {:chaptype => "pre"})
  end

  if @params["creditfile"].present? && File.exist?(@params["creditfile"])
    FileUtils.cp(@params["creditfile"], "#{basetmpdir}/#{File.basename(@params["creditfile"])}")
    @htmltoc.add_item(1, File.basename(@params["creditfile"]), @producer.res.v("credittitle"), {:chaptype => "pre"})
  end
end
copy_images(resdir, destdir, allow_exts=nil) click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 170
def copy_images(resdir, destdir, allow_exts=nil)
  return nil unless File.exist?(resdir)
  allow_exts = @params["image_ext"] if allow_exts.nil?
  FileUtils.mkdir_p(destdir)
  if @params["epubmaker"]["verify_target_images"].present?
    @params["epubmaker"]["force_include_images"].each do |file|
      unless File.exist?(file)
        warn "#{file} is not found, skip." if file !~ /\Ahttp[s]?:/
        next
      end
      basedir = File.dirname(file)
      FileUtils.mkdir_p("#{destdir}/#{basedir}")
      log("Copy #{file} to the temporary directory.")
      FileUtils.cp(file, "#{destdir}/#{basedir}")
    end
  else
    recursive_copy_files(resdir, destdir, allow_exts)
  end
end
copy_resources(resdir, destdir, allow_exts=nil) click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 190
def copy_resources(resdir, destdir, allow_exts=nil)
  return nil unless File.exist?(resdir)
  allow_exts = @params["image_ext"] if allow_exts.nil?
  FileUtils.mkdir_p(destdir)
  recursive_copy_files(resdir, destdir, allow_exts)
end
copy_stylesheet(basetmpdir) click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 404
def copy_stylesheet(basetmpdir)
  if @params["stylesheet"].size > 0
    @params["stylesheet"].each do |sfile|
      FileUtils.cp(sfile, basetmpdir)
      @producer.contents.push(Content.new("file" => sfile))
    end
  end
end
detect_properties(path) click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 350
def detect_properties(path)
  properties = []
  File.open(path) do |f|
    doc = REXML::Document.new(f)
    if REXML::XPath.first(doc, "//m:math", {'m' => 'http://www.w3.org/1998/Math/MathML'})
      properties<< "mathml"
    end
    if REXML::XPath.first(doc, "//s:svg", {'s' => 'http://www.w3.org/2000/svg'})
      properties<< "svg"
    end
  end
  properties
end
load_yaml(yamlfile) click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 42
def load_yaml(yamlfile)
  loader = ReVIEW::YAMLLoader.new
  @params = ReVIEW::Configure.values.deep_merge(loader.load_file(yamlfile))
  @producer = Producer.new(@params)
  @producer.load(yamlfile)
  @params = @producer.params
  @params.maker = "epubmaker"
end
log(s) click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 38
def log(s)
  puts s if @params["debug"].present?
end
produce(yamlfile, bookname=nil) click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 64
def produce(yamlfile, bookname=nil)
  load_yaml(yamlfile)
  I18n.setup(@params["language"])
  bookname = @params["bookname"] if bookname.nil?
  booktmpname = "#{bookname}-epub"

  begin
    @params.check_version(ReVIEW::VERSION)
  rescue ReVIEW::ConfigError => e
    warn e.message
  end
  log("Loaded yaml file (#{yamlfile}). I will produce #{bookname}.epub.")

  FileUtils.rm_f("#{bookname}.epub")
  FileUtils.rm_rf(booktmpname) if @params["debug"]

  basetmpdir = build_path()
  begin
    log("Created first temporary directory as #{basetmpdir}.")

    call_hook("hook_beforeprocess", basetmpdir)

    @htmltoc = ReVIEW::HTMLToc.new(basetmpdir)
    ## copy all files into basetmpdir
    copy_stylesheet(basetmpdir)

    copy_frontmatter(basetmpdir)
    call_hook("hook_afterfrontmatter", basetmpdir)

    build_body(basetmpdir, yamlfile)
    call_hook("hook_afterbody", basetmpdir)

    copy_backmatter(basetmpdir)
    call_hook("hook_afterbackmatter", basetmpdir)

    ## push contents in basetmpdir into @producer
    push_contents(basetmpdir)

    if @params["epubmaker"]["verify_target_images"].present?
      verify_target_images(basetmpdir)
      copy_images(@params["imagedir"], basetmpdir)
    else
      copy_images(@params["imagedir"], "#{basetmpdir}/images")
    end

    copy_resources("covers", "#{basetmpdir}/images")
    copy_resources("adv", "#{basetmpdir}/images")
    copy_resources(@params["fontdir"], "#{basetmpdir}/fonts", @params["font_ext"])

    call_hook("hook_aftercopyimage", basetmpdir)

    @producer.import_imageinfo("#{basetmpdir}/images", basetmpdir)
    @producer.import_imageinfo("#{basetmpdir}/fonts", basetmpdir, @params["font_ext"])

    epubtmpdir = nil
    if @params["debug"].present?
      epubtmpdir = "#{basetmpdir}/#{booktmpname}"
      Dir.mkdir(epubtmpdir)
    end
    log("Call ePUB producer.")
    @producer.produce("#{bookname}.epub", basetmpdir, epubtmpdir)
    log("Finished.")
  ensure
    unless @params["debug"]
      FileUtils.remove_entry_secure basetmpdir
    end
  end
end
push_contents(basetmpdir) click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 385
def push_contents(basetmpdir)
  @htmltoc.each_item do |level, file, title, args|
    next if level.to_i > @params["toclevel"] && args[:force_include].nil?
    log("Push #{file} to ePUB contents.")

    hash = {"file" => file, "level" => level.to_i, "title" => title, "chaptype" => args[:chaptype]}
    if args[:id].present?
      hash["id"] = args[:id]
    end
    if args[:properties].present?
      hash["properties"] = args[:properties].split(" ")
    end
    if args[:notoc].present?
      hash["notoc"] = args[:notoc]
    end
    @producer.contents.push(Content.new(hash))
  end
end
recursive_copy_files(resdir, destdir, allow_exts) click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 197
def recursive_copy_files(resdir, destdir, allow_exts)
  Dir.open(resdir) do |dir|
    dir.each do |fname|
      next if fname.start_with?('.')
      if FileTest.directory?("#{resdir}/#{fname}")
        recursive_copy_files("#{resdir}/#{fname}", "#{destdir}/#{fname}", allow_exts)
      else
        if fname =~ /\.(#{allow_exts.join("|")})\Z/
          FileUtils.mkdir_p(destdir)
          log("Copy #{resdir}/#{fname} to the temporary directory.")
          FileUtils.cp("#{resdir}/#{fname}", destdir)
        end
      end
    end
  end
end
remove_hidden_title(basetmpdir, htmlfile) click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 339
def remove_hidden_title(basetmpdir, htmlfile)
  File.open("#{basetmpdir}/#{htmlfile}", "r+") do |f|
    body = f.read.
           gsub(/<h\d .*?hidden=['"]true['"].*?>.*?<\/h\d>\n/, '').
           gsub(/(<h\d .*?)\s*notoc=['"]true['"]\s*(.*?>.*?<\/h\d>\n)/, '\1\2')
    f.rewind
    f.print body
    f.truncate(f.tell)
  end
end
template_name() click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 278
def template_name
  if @producer.params["htmlversion"].to_i == 5
    './html/layout-html5.html.erb'
  else
    './html/layout-xhtml1.html.erb'
  end
end
verify_target_images(basetmpdir) click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 145
def verify_target_images(basetmpdir)
  @producer.contents.each do |content|
    if content.media == "application/xhtml+xml"

      File.open("#{basetmpdir}/#{content.file}") do |f|
        Document.new(File.new(f)).each_element("//img") do |e|
          @params["epubmaker"]["force_include_images"].push(e.attributes["src"])
          if e.attributes["src"] =~ /svg\Z/
            content.properties.push("svg")
          end
        end
      end
    elsif content.media == "text/css"
      File.open("#{basetmpdir}/#{content.file}") do |f|
        f.each_line do |l|
          l.scan(/url\((.+?)\)/) do |m|
            @params["epubmaker"]["force_include_images"].push($1.strip)
          end
        end
      end
    end
  end
  @params["epubmaker"]["force_include_images"] = @params["epubmaker"]["force_include_images"].sort.uniq
end
write_buildlogtxt(basetmpdir, htmlfile, reviewfile) click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 485
def write_buildlogtxt(basetmpdir, htmlfile, reviewfile)
  File.open("#{basetmpdir}/#{@buildlogtxt}", "a") do |f|
    f.puts "#{htmlfile},#{reviewfile}"
  end
end
write_info_body(basetmpdir, id, filename, ispart=nil, chaptype=nil) click to toggle source
# File ../../../../../lib/review/epubmaker.rb, line 364
def write_info_body(basetmpdir, id, filename, ispart=nil, chaptype=nil)
  headlines = []
  path = File.join(basetmpdir, filename)
  Document.parse_stream(File.new(path), ReVIEWHeaderListener.new(headlines))
  properties = detect_properties(path)
  prop_str = ""
  if properties.present?
    prop_str = ",properties="+properties.join(" ")
  end
  first = true
  headlines.each do |headline|
    headline["level"] = 0 if ispart.present? && headline["level"] == 1
    if first.nil?
      @htmltoc.add_item(headline["level"], filename+"#"+headline["id"], headline["title"], {:chaptype => chaptype, :notoc => headline["notoc"]})
    else
      @htmltoc.add_item(headline["level"], filename, headline["title"], {:force_include => true, :chaptype => chaptype+prop_str, :notoc => headline["notoc"]})
      first = nil
    end
  end
end