lib/review/epubmaker.rb in review-1.2.0 vs lib/review/epubmaker.rb in review-1.3.0

- old
+ new

@@ -18,10 +18,11 @@ include REXML def initialize @epub = nil @tochtmltxt = "toc-html.txt" + @buildlogtxt = "build-log.txt" end def log(s) puts s unless @params["debug"].nil? end @@ -34,15 +35,17 @@ end def produce(yamlfile, bookname=nil) load_yaml(yamlfile) bookname = @params["bookname"] if bookname.nil? + booktmpname = "#{bookname}-epub" + log("Loaded yaml file (#{yamlfile}). I will produce #{bookname}.epub.") File.unlink("#{bookname}.epub") if File.exist?("#{bookname}.epub") - FileUtils.rm_rf(bookname) if @params["debug"] && File.exist?(bookname) - + FileUtils.rm_rf(booktmpname) if @params["debug"] && File.exist?(booktmpname) + Dir.mktmpdir(bookname, Dir.pwd) do |basetmpdir| log("Created first temporary directory as #{basetmpdir}.") log("Call hook_beforeprocess. (#{@params["hook_beforeprocess"]})") call_hook(@params["hook_beforeprocess"], basetmpdir) @@ -61,53 +64,109 @@ log("Call hook_afterbackmatter. (#{@params["hook_afterbackmatter"]})") call_hook(@params["hook_afterbackmatter"], basetmpdir) push_contents(basetmpdir) - copy_images(@params["imagedir"], "#{basetmpdir}/images") - copy_images("covers", "#{basetmpdir}/images") - copy_images("adv", "#{basetmpdir}/images") - copy_images(@params["fontdir"], "#{basetmpdir}/fonts", @params["font_ext"]) + if !@params["verify_target_images"].nil? + 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"]) + log("Call hook_aftercopyimage. (#{@params["hook_aftercopyimage"]})") call_hook(@params["hook_aftercopyimage"], basetmpdir) @epub.import_imageinfo("#{basetmpdir}/images", basetmpdir) @epub.import_imageinfo("#{basetmpdir}/fonts", basetmpdir, @params["font_ext"]) - epubtmpdir = @params["debug"].nil? ? nil : "#{Dir.pwd}/#{bookname}" - Dir.mkdir(bookname) unless @params["debug"].nil? + epubtmpdir = @params["debug"].nil? ? nil : "#{Dir.pwd}/#{booktmpname}" + Dir.mkdir(booktmpname) unless @params["debug"].nil? log("Call ePUB producer.") @epub.produce("#{bookname}.epub", basetmpdir, epubtmpdir) log("Finished.") end end def call_hook(filename, *params) if !filename.nil? && File.exist?(filename) && FileTest.executable?(filename) - system(filename, *params) + if ENV["REVIEW_SAFE_MODE"].to_i & 1 > 0 + warn "hook is prohibited in safe mode. ignored." + else + system(filename, *params) + end end end - def copy_images(imagedir, destdir, allow_exts=nil) - return nil unless File.exist?(imagedir) + def verify_target_images(basetmpdir) + @epub.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["force_include_images"].push(e.attributes["src"]) + if e.attributes["src"] =~ /svg\Z/i + 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["force_include_images"].push($1.strip) + end + end + end + end + end + @params["force_include_images"] = @params["force_include_images"].sort.uniq + end + + 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) unless FileTest.directory?(destdir) - recursive_copy_images(imagedir, destdir, allow_exts) + if !@params["verify_target_images"].nil? + @params["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}") unless FileTest.directory?("#{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 - def recursive_copy_images(imagedir, destdir, allow_exts) - Dir.open(imagedir) do |dir| + 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) unless FileTest.directory?(destdir) + recursive_copy_files(resdir, destdir, allow_exts) + end + + def recursive_copy_files(resdir, destdir, allow_exts) + Dir.open(resdir) do |dir| dir.each do |fname| next if fname =~ /\A\./ - if FileTest.directory?("#{imagedir}/#{fname}") - recursive_copy_images("#{imagedir}/#{fname}", "#{destdir}/#{fname}", allow_exts) + if FileTest.directory?("#{resdir}/#{fname}") + recursive_copy_files("#{resdir}/#{fname}", "#{destdir}/#{fname}", allow_exts) else if fname =~ /\.(#{allow_exts.join("|")})\Z/i Dir.mkdir(destdir) unless File.exist?(destdir) - log("Copy #{imagedir}/#{fname} to the temporary directory.") - FileUtils.cp("#{imagedir}/#{fname}", destdir) + log("Copy #{resdir}/#{fname} to the temporary directory.") + FileUtils.cp("#{resdir}/#{fname}", destdir) end end end end end @@ -132,18 +191,19 @@ 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? - write_tochtmltxt(basetmpdir, "0\t#{htmlfile}\t#{title}") + write_tochtmltxt(basetmpdir, "0\t#{htmlfile}\t#{title}\tchaptype=part") + write_buildlogtxt(basetmpdir, htmlfile, "") end end part.chapters.each do |chap| build_chap(chap, base_path, basetmpdir, yamlfile, nil) end - + end end def build_part(part, basetmpdir, htmlfile) log("Create #{htmlfile} from a template.") @@ -156,20 +216,26 @@ if part.name.strip.present? f.puts <<EOT <h2 class="part-title">#{part.name.strip}</h2> EOT end - + f.puts <<EOT </div> EOT f.puts footer end end def build_chap(chap, base_path, basetmpdir, yamlfile, ispart=nil) filename = "" + + chaptype = "body" + chaptype = "part" unless ispart.nil? + chaptype = "pre" if chap.on_PREDEF? + chaptype = "post" if chap.on_POSTDEF? + if !ispart.nil? filename = chap.path else filename = Pathname.new(chap.path).relative_path_from(base_path).to_s end @@ -187,52 +253,57 @@ id = sprintf("chap%02d", @bodycount) end end htmlfile = "#{id}.#{@params["htmlext"]}" + write_buildlogtxt(basetmpdir, htmlfile, filename) log("Create #{htmlfile} from #{filename}.") level = @params["secnolevel"] - - if !ispart.nil? - level = @params["part_secnolevel"] - else - level = @params["pre_secnolevel"] if chap.on_PREDEF? - level = @params["post_secnolevel"] if chap.on_POSTDEF? - end +# TODO: It would be nice if we can modify level in PART, PREDEF, or POSTDEF. +# But we have to care about section number reference (@<hd>) also. +# +# if !ispart.nil? +# level = @params["part_secnolevel"] +# else +# level = @params["pre_secnolevel"] if chap.on_PREDEF? +# level = @params["post_secnolevel"] if chap.on_POSTDEF? +# end + stylesheet = "" if @params["stylesheet"].size > 0 stylesheet = "--stylesheet=#{@params["stylesheet"].join(",")}" end - system("review-compile --yaml=#{yamlfile} --target=html --level=#{level} --htmlversion=#{@params["htmlversion"]} --epubversion=#{@params["epubversion"]} #{stylesheet} #{@params["params"]} #{filename} > \"#{basetmpdir}/#{htmlfile}\"") + system("#{ReVIEW::MakerHelper.bindir}/review-compile --yaml=#{yamlfile} --target=html --level=#{level} --htmlversion=#{@params["htmlversion"]} --epubversion=#{@params["epubversion"]} #{stylesheet} #{@params["params"]} #{filename} > \"#{basetmpdir}/#{htmlfile}\"") - write_info_body(basetmpdir, id, htmlfile, ispart) + write_info_body(basetmpdir, id, htmlfile, ispart, chaptype) end - def write_info_body(basetmpdir, id, filename, ispart=nil) + def write_info_body(basetmpdir, id, filename, ispart=nil, chaptype=nil) headlines = [] # FIXME:nonumを修正する必要あり Document.parse_stream(File.new("#{basetmpdir}/#{filename}"), ReVIEWHeaderListener.new(headlines)) first = true headlines.each do |headline| headline["level"] = 0 if !ispart.nil? && headline["level"] == 1 if first.nil? - write_tochtmltxt(basetmpdir, "#{headline["level"]}\t#{filename}##{headline["id"]}\t#{headline["title"]}") + write_tochtmltxt(basetmpdir, "#{headline["level"]}\t#{filename}##{headline["id"]}\t#{headline["title"]}\tchaptype=#{chaptype}") else - write_tochtmltxt(basetmpdir, "#{headline["level"]}\t#{filename}\t#{headline["title"]}\tforce_include=true") + write_tochtmltxt(basetmpdir, "#{headline["level"]}\t#{filename}\t#{headline["title"]}\tforce_include=true,chaptype=#{chaptype}") first = nil end end end def push_contents(basetmpdir) File.open("#{basetmpdir}/#{@tochtmltxt}") do |f| f.each_line do |l| force_include = nil customid = nil + chaptype = nil level, file, title, custom = l.chomp.split("\t") unless custom.nil? # custom setting vars = custom.split(/,\s*/) vars.each do |var| @@ -240,20 +311,22 @@ case k when "id" customid = v when "force_include" force_include = true + when "chaptype" + chaptype = v end end end next if level.to_i > @params["toclevel"] && force_include.nil? log("Push #{file} to ePUB contents.") if customid.nil? - @epub.contents.push(Content.new("file" => file, "level" => level.to_i, "title" => title)) + @epub.contents.push(Content.new("file" => file, "level" => level.to_i, "title" => title, "chaptype" => chaptype)) else - @epub.contents.push(Content.new("id" => customid, "file" => file, "level" => level.to_i, "title" => title)) + @epub.contents.push(Content.new("id" => customid, "file" => file, "level" => level.to_i, "title" => title, "chaptype" => chaptype)) end end end end @@ -268,26 +341,26 @@ def copy_frontmatter(basetmpdir) FileUtils.cp(@params["cover"], "#{basetmpdir}/#{File.basename(@params["cover"])}") if !@params["cover"].nil? && File.exist?(@params["cover"]) if @params["titlepage"] - if @params["titlepagefile"].nil? + if @params["titlefile"].nil? build_titlepage(basetmpdir, "titlepage.#{@params["htmlext"]}") else - FileUtils.cp(@params["titlepagefile"], "titlepage.#{@params["htmlext"]}") + FileUtils.cp(@params["titlefile"], "#{basetmpdir}/titlepage.#{@params["htmlext"]}") end - write_tochtmltxt(basetmpdir, "1\ttitlepage.#{@params["htmlext"]}\t#{@epub.res.v("titlepagetitle")}") + write_tochtmltxt(basetmpdir, "1\ttitlepage.#{@params["htmlext"]}\t#{@epub.res.v("titlepagetitle")}\tchaptype=pre") end if !@params["originaltitlefile"].nil? && File.exist?(@params["originaltitlefile"]) FileUtils.cp(@params["originaltitlefile"], "#{basetmpdir}/#{File.basename(@params["originaltitlefile"])}") - write_tochtmltxt(basetmpdir, "1\t#{File.basename(@params["originaltitlefile"])}\t#{@epub.res.v("originaltitle")}") + write_tochtmltxt(basetmpdir, "1\t#{File.basename(@params["originaltitlefile"])}\t#{@epub.res.v("originaltitle")}\tchaptype=pre") end if !@params["creditfile"].nil? && File.exist?(@params["creditfile"]) FileUtils.cp(@params["creditfile"], "#{basetmpdir}/#{File.basename(@params["creditfile"])}") - write_tochtmltxt(basetmpdir, "1\t#{File.basename(@params["creditfile"])}\t#{@epub.res.v("credittitle")}") + write_tochtmltxt(basetmpdir, "1\t#{File.basename(@params["creditfile"])}\t#{@epub.res.v("credittitle")}\tchaptype=pre") end end def build_titlepage(basetmpdir, htmlfile) File.open("#{basetmpdir}/#{htmlfile}", "w") do |f| @@ -316,34 +389,45 @@ end def copy_backmatter(basetmpdir) if @params["profile"] FileUtils.cp(@params["profile"], "#{basetmpdir}/#{File.basename(@params["profile"])}") - write_tochtmltxt(basetmpdir, "1\t#{File.basename(@params["profile"])}\t#{@epub.res.v("profiletitle")}") + write_tochtmltxt(basetmpdir, "1\t#{File.basename(@params["profile"])}\t#{@epub.res.v("profiletitle")}\tchaptype=post") end if @params["advfile"] FileUtils.cp(@params["advfile"], "#{basetmpdir}/#{File.basename(@params["advfile"])}") - write_tochtmltxt(basetmpdir, "1\t#{File.basename(@params["advfile"])}\t#{@epub.res.v("advtitle")}") + write_tochtmltxt(basetmpdir, "1\t#{File.basename(@params["advfile"])}\t#{@epub.res.v("advtitle")}\tchaptype=post") end if @params["colophon"] if @params["colophon"].instance_of?(String) # FIXME:このやり方はやめる? FileUtils.cp(@params["colophon"], "#{basetmpdir}/colophon.#{@params["htmlext"]}") else File.open("#{basetmpdir}/colophon.#{@params["htmlext"]}", "w") {|f| @epub.colophon(f) } end - write_tochtmltxt(basetmpdir, "1\tcolophon.#{@params["htmlext"]}\t#{@epub.res.v("colophontitle")}") + write_tochtmltxt(basetmpdir, "1\tcolophon.#{@params["htmlext"]}\t#{@epub.res.v("colophontitle")}\tchaptype=post") end + + if @params["backcover"] + FileUtils.cp(@params["backcover"], "#{basetmpdir}/#{File.basename(@params["backcover"])}") + write_tochtmltxt(basetmpdir, "1\t#{File.basename(@params["backcover"])}\t#{@epub.res.v("backcovertitle")}\tchaptype=post") + end end def write_tochtmltxt(basetmpdir, s) File.open("#{basetmpdir}/#{@tochtmltxt}", "a") do |f| f.puts s end end + def write_buildlogtxt(basetmpdir, htmlfile, reviewfile) + File.open("#{basetmpdir}/#{@buildlogtxt}", "a") do |f| + f.puts "#{htmlfile},#{reviewfile}" + end + end + def header(title) # titleはすでにエスケープ済みと想定 s = <<EOT <?xml version="1.0" encoding="UTF-8"?> EOT @@ -390,11 +474,11 @@ def initialize(headlines) @level = nil @content = "" @headlines = headlines end - + def tag_start(name, attrs) if name =~ /\Ah(\d+)/ unless @level.nil? raise "#{name}, #{attrs}" end @@ -406,19 +490,19 @@ elsif name == "a" && !attrs["id"].nil? @id = attrs["id"] end end end - + def tag_end(name) if name =~ /\Ah\d+/ @headlines.push({"level" => @level, "id" => @id, "title" => @content}) unless @id.nil? @content = "" @level = nil @id = nil end end - + def text(text) unless @level.nil? @content << text.gsub("\t", " ") # FIXME:区切り文字 end end