diff --git a/bin/review-webmaker b/bin/review-webmaker new file mode 100644 index 000000000..18d9f38f9 --- /dev/null +++ b/bin/review-webmaker @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby +# +# This program is free software. +# You can distribute or modify this program under the terms of +# the GNU LGPL, Lesser General Public License version 2.1. +# For details of the GNU LGPL, see the file "COPYING". +# + +require 'pathname' +bindir = Pathname.new(__FILE__).realpath.dirname +$LOAD_PATH.unshift((bindir + '../lib').realpath) + +require 'review/webmaker' + +ReVIEW::WEBMaker.execute(*ARGV) diff --git a/lib/review/configure.rb b/lib/review/configure.rb index 3a46181eb..c94819e2f 100644 --- a/lib/review/configure.rb +++ b/lib/review/configure.rb @@ -40,6 +40,9 @@ def self.values "mathml" => nil, # for HTML "htmlext" => "html", "htmlversion" => 5, + "imagedir" => "images", + "image_ext" => %w(png gif jpg jpeg svg ttf woff otf), + "fontdir" => "fonts", "chapter_file" => 'CHAPS', "part_file" => 'PART', diff --git a/lib/review/htmlbuilder.rb b/lib/review/htmlbuilder.rb index 7d60bcb02..8a02499cf 100644 --- a/lib/review/htmlbuilder.rb +++ b/lib/review/htmlbuilder.rb @@ -13,6 +13,7 @@ require 'review/htmlutils' require 'review/template' require 'review/textutils' +require 'review/webtocprinter' module ReVIEW @@ -54,17 +55,25 @@ def builder_init_file @sec_counter = SecCounter.new(5, @chapter) @nonum_counter = 0 @body_ext = nil + @toc = nil end private :builder_init_file def result + if @book.config.maker == "webmaker" + htmldir = "web/html" + localfilename = "layout-web.html.erb" + else + htmldir = "html" + localfilename = "layout.html.erb" + end if @book.htmlversion == 5 - htmlfilename = "./html/layout-html5.html.erb" + htmlfilename = File.join(htmldir, "layout-html5.html.erb") else - htmlfilename = "./html/layout-xhtml1.html.erb" + htmlfilename = File.join(htmldir, "layout-xhtml1.html.erb") end - layout_file = File.join(@book.basedir, "layouts", "layout.html.erb") + layout_file = File.join(@book.basedir, "layouts", localfilename) if !File.exist?(layout_file) && File.exist?(File.join(@book.basedir, "layouts", "layout.erb")) raise ReVIEW::ConfigError, "layout.erb is obsoleted. Please use layout.html.erb." end @@ -86,6 +95,12 @@ def result @stylesheets = @book.config["stylesheet"] @next = @chapter.next_chapter @prev = @chapter.prev_chapter + @next_title = @next ? compile_inline(@next.title) : "" + @prev_title = @prev ? compile_inline(@prev.title) : "" + + if @book.config.maker == "webmaker" + @toc = ReVIEW::WEBTOCPrinter.book_to_string(@book) + end tmpl = ReVIEW::Template.load(layout_file) tmpl.result(binding) diff --git a/lib/review/htmlutils.rb b/lib/review/htmlutils.rb index 489070f38..41acf493f 100644 --- a/lib/review/htmlutils.rb +++ b/lib/review/htmlutils.rb @@ -25,6 +25,7 @@ def escape_html(str) end alias_method :escape, :escape_html + alias_method :h, :escape_html def unescape_html(str) # FIXME better code diff --git a/lib/review/webmaker.rb b/lib/review/webmaker.rb new file mode 100644 index 000000000..5221923a9 --- /dev/null +++ b/lib/review/webmaker.rb @@ -0,0 +1,297 @@ +# encoding: utf-8 +# +# This program is free software. +# You can distribute or modify this program under the terms of +# the GNU LGPL, Lesser General Public License version 2.1. +# For details of the GNU LGPL, see the file "COPYING". +# +require 'optparse' +require 'yaml' +require 'fileutils' +require 'erb' + +require 'review' +require 'review/i18n' +require 'review/converter' +require 'erb' + +module ReVIEW + class WEBMaker + include ERB::Util + + attr_accessor :config, :basedir + + def initialize + @basedir = nil + end + + def self.execute(*args) + self.new.execute(*args) + end + + def parse_opts(args) + cmd_config = Hash.new + opts = OptionParser.new + + opts.banner = "Usage: review-webmaker configfile" + opts.version = ReVIEW::VERSION + opts.on('--help', 'Prints this message and quit.') do + puts opts.help + exit 0 + end + opts.on('--ignore-errors', 'Ignore review-compile errors.') do + cmd_config["ignore-errors"] = true + end + + opts.parse!(args) + if args.size != 1 + puts opts.help + exit 0 + end + + return cmd_config, args[0] + end + + def build_path + @config["docroot"] || "webroot" + end + + def remove_old_files(path) + FileUtils.rm_rf(path) + end + + + def execute(*args) + @config = ReVIEW::Configure.values + @config.maker = "webmaker" + cmd_config, yamlfile = parse_opts(args) + + @config.merge!(YAML.load_file(yamlfile)) + # YAML configs will be overridden by command line options. + @config.merge!(cmd_config) + @config["htmlext"] = "html" + I18n.setup(@config["language"]) + generate_html_files(yamlfile) + end + + def generate_html_files(yamlfile) + @basedir = File.dirname(yamlfile) + @path = build_path() + remove_old_files(@path) + Dir.mkdir(@path) + + @book = ReVIEW::Book.load(@basedir) + @book.config = @config + + copy_stylesheet(@path) + copy_frontmatter(@path) + build_body(@path, yamlfile) + copy_backmatter(@path) + + copy_images(@config["imagedir"], "#{@path}/images") + + copy_resources("covers", "#{@path}/images") + copy_resources("adv", "#{@path}/images") + copy_resources(@config["fontdir"], "#{@path}/fonts", @config["font_ext"]) + end + + def build_body(basetmpdir, yamlfile) + base_path = Pathname.new(@basedir) + builder = ReVIEW::HTMLBuilder.new + @converter = ReVIEW::Converter.new(@book, builder) + @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}.#{@config["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? + end + end + + part.chapters.each do |chap| + build_chap(chap, base_path, basetmpdir, false) + end + + end + end + + def build_part(part, basetmpdir, htmlfile) + File.open("#{basetmpdir}/#{htmlfile}", "w") do |f| + @body = "" + @body << "
\n" + @body << "

#{ReVIEW::I18n.t("part", part.number)}

\n" + if part.name.strip.present? + @body << "

#{part.name.strip}

\n" + end + @body << "
\n" + + @language = @config['language'] + @stylesheets = @config["stylesheet"] + tmplfile = File.expand_path(template_name, ReVIEW::Template::TEMPLATE_DIR) + tmpl = ReVIEW::Template.load(tmplfile) + f.write tmpl.result(binding) + end + end + + def template_name + if @config["htmlversion"].to_i == 5 + 'web/html/layout-html5.html.erb' + else + 'web/html/layout-xhtml1.html.erb' + end + end + + def build_chap(chap, base_path, basetmpdir, ispart) + filename = "" + + 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/, "") + + htmlfile = "#{id}.#{@config["htmlext"]}" + + if @config["params"].present? + warn "'params:' in config.yml is obsoleted." + end + + begin + @converter.convert(filename, File.join(basetmpdir, htmlfile)) + rescue => e + warn "compile error in #{filename} (#{e.class})" + warn e.message + end + end + + def copy_images(resdir, destdir) + return nil unless File.exist?(resdir) + allow_exts = @config["image_ext"] + FileUtils.mkdir_p(destdir) + recursive_copy_files(resdir, destdir, allow_exts) + end + + def copy_resources(resdir, destdir, allow_exts=nil) + if !resdir || !File.exist?(resdir) + return nil + end + allow_exts = @config["image_ext"] if allow_exts.nil? + FileUtils.mkdir_p(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.start_with?('.') + if FileTest.directory?("#{resdir}/#{fname}") + recursive_copy_files("#{resdir}/#{fname}", "#{destdir}/#{fname}", allow_exts) + else + if fname =~ /\.(#{allow_exts.join("|")})\Z/i + FileUtils.mkdir_p(destdir) + FileUtils.cp("#{resdir}/#{fname}", destdir) + end + end + end + end + end + + def copy_stylesheet(basetmpdir) + if @config["stylesheet"].size > 0 + @config["stylesheet"].each do |sfile| + FileUtils.cp(sfile, basetmpdir) + end + end + end + + def copy_frontmatter(basetmpdir) + build_indexpage(basetmpdir) + + if @config["titlepage"] + if @config["titlefile"] + FileUtils.cp(@config["titlefile"], "#{basetmpdir}/titlepage.#{@config["htmlext"]}") + else + build_titlepage(basetmpdir, "titlepage.#{@config["htmlext"]}") + end + end + + copy_file_with_param("creditfile") + copy_file_with_param("originaltitlefile") + end + + def build_indexpage(basetmpdir) + File.open("#{basetmpdir}/index.html", "w") do |f| + if @config["coverimage"] + file = File.join("images", @config["coverimage"]) + @body = <<-EOT +
+ +
+ EOT + else + @body = "" + end + @language = @config['language'] + @stylesheets = @config["stylesheet"] + @toc = ReVIEW::WEBTOCPrinter.book_to_string(@book) + @next = @book.chapters[0] + @next_title = @next ? @next.title : "" + tmplfile = File.expand_path(template_name, ReVIEW::Template::TEMPLATE_DIR) + tmpl = ReVIEW::Template.load(tmplfile) + f.write tmpl.result(binding) + end + end + + def build_titlepage(basetmpdir, htmlfile) + File.open("#{basetmpdir}/#{htmlfile}", "w") do |f| + @body = "" + @body << "
" + @body << "

#{CGI.escapeHTML(@config["booktitle"])}

" + if @config["aut"] + @body << "

#{join_with_separator(@config["aut"], ReVIEW::I18n.t("names_splitter"))}

" + end + if @config["prt"] + @body << "

#{join_with_separator(@config["prt"], ReVIEW::I18n.t("names_splitter"))}

" + end + @body << "
" + + @language = @config['language'] + @stylesheets = @config["stylesheet"] + tmplfile = File.expand_path(template_name, ReVIEW::Template::TEMPLATE_DIR) + tmpl = ReVIEW::Template.load(tmplfile) + f.write tmpl.result(binding) + end + end + + def copy_backmatter(basetmpdir) + copy_file_with_param("profile") + copy_file_with_param("advfile") + if @config["colophon"] && @config["colophon"].kind_of?(String) + copy_file_with_param("colophon", "colophon.#{@config["htmlext"]}") + end + copy_file_with_param("backcover") + end + + def copy_file_with_param(name, target_file = nil) + if @config[name] && File.exist?(@config[name]) + target_file ||= File.basename(@config[name]) + FileUtils.cp(@config[name], File.join(basetmpdir, target_file)) + end + end + + def join_with_separator(value, sep) + if value.kind_of? Array + value.join(sep) + else + value + end + end + + end +end + diff --git a/lib/review/webtocprinter.rb b/lib/review/webtocprinter.rb new file mode 100644 index 000000000..121d720bc --- /dev/null +++ b/lib/review/webtocprinter.rb @@ -0,0 +1,49 @@ +require 'review' +require 'review/tocprinter' + +module ReVIEW + class WEBTOCPrinter < TOCPrinter + include HTMLUtils + + def self.book_to_string(book) + io = StringIO.new + ReVIEW::WEBTOCPrinter.new(1, {}, io).print_book(book) + io.seek(0) + io.read + end + + def print_book(book) + @out.puts '' + end + + def print_part(part) + if part.number + @out.puts "
  • #{h(part.title)}\n\n
  • \n" + end + end + + def print_chapter(chap) + chap_node = TOCParser.chapter_node(chap) + ext = chap.book.config["htmlext"] || "html" + path = chap.path.sub(/\.re/, "."+ext) + if chap_node.number && chap.on_CHAPS? + label = "#{chap.number} #{chap.title}" + else + label = chap.title + end + @out.puts "
  • #{h(label)}
  • \n" + end + + end +end diff --git a/templates/web/html/layout-html5.html.erb b/templates/web/html/layout-html5.html.erb new file mode 100644 index 000000000..358dab317 --- /dev/null +++ b/templates/web/html/layout-html5.html.erb @@ -0,0 +1,56 @@ + + + + + +<% if @stylesheets.present? %> +<% @stylesheets.each do |style| %> + +<% end %> +<% end%> +<% if @next.present? %>"><% end %> +<% if @prev.present? %>"><% end %> + + <%=h @title %> | <%=h @book.config["booktitle"]%> + +> +
    + +
    +
    +
    +
    + <%= @body %> +
    + + +
    +
    + + <% if @javascripts.present? %> + <% @javascripts.each do |script| %> + + <% end %> + <% end%> + + diff --git a/templates/web/html/layout-xhtml1.html.erb b/templates/web/html/layout-xhtml1.html.erb new file mode 100644 index 000000000..a88a8d10d --- /dev/null +++ b/templates/web/html/layout-xhtml1.html.erb @@ -0,0 +1,20 @@ + + + + + + +<% if @stylesheets.present? %> +<% @stylesheets.each do |style| %> + +<% end %> +<% end%> + + <%= @title %> + +> +<% if @error_messages %><%= @error_messages %><% end %> +<% if @warning_messages %><%= @warning_messages %><% end %> +<%= @body %> + + diff --git a/test/test_i18n.rb b/test/test_i18n.rb index f8ed318e8..35e85e458 100644 --- a/test/test_i18n.rb +++ b/test/test_i18n.rb @@ -194,11 +194,11 @@ def test_htmlbuilder def _setup_htmlbuilder I18n.setup "en" @builder = HTMLBuilder.new() - @config = { + @config = ReVIEW::Configure[ "secnolevel" => 2, # for IDGXMLBuilder, HTMLBuilder "stylesheet" => nil, # for HTMLBuilder "ext" => ".re" - } + ] @book = Book::Base.new(".") @book.config = @config @compiler = ReVIEW::Compiler.new(@builder)