# Copyright (c) 2008-2018 Minero Aoki, Kenshi Muto, Masayoshi Takahashi, # KADO Masanori # 2002-2007 Minero Aoki # # 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. # require 'review/builder' require 'review/htmlutils' require 'review/template' require 'review/textutils' require 'review/webtocprinter' require 'digest' require 'tmpdir' require 'open3' module ReVIEW class HTMLBuilder < Builder include TextUtils include HTMLUtils [:ref].each do |e| Compiler.definline(e) end Compiler.defblock(:planning, 0..1) Compiler.defblock(:best, 0..1) Compiler.defblock(:security, 0..1) Compiler.defblock(:point, 0..1) Compiler.defblock(:shoot, 0..1) def pre_paragraph '
' end def post_paragraph '
' end def extname ".#{@book.config['htmlext']}" end def builder_init end private :builder_init def builder_init_file @noindent = nil @ol_num = nil @warns = [] @errors = [] @chapter.book.image_types = %w[.png .jpg .jpeg .gif .svg] @column = 0 @sec_counter = SecCounter.new(5, @chapter) @nonum_counter = 0 @first_line_num = nil @body_ext = nil @toc = nil end private :builder_init_file def layoutfile 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 = File.join(htmldir, 'layout-html5.html.erb') else htmlfilename = File.join(htmldir, 'layout-xhtml1.html.erb') end 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 if File.exist?(layout_file) if ENV['REVIEW_SAFE_MODE'].to_i & 4 > 0 warn %Q(user's layout is prohibited in safe mode. ignored.) layout_file = File.expand_path(htmlfilename, ReVIEW::Template::TEMPLATE_DIR) end else layout_file = File.expand_path(htmlfilename, ReVIEW::Template::TEMPLATE_DIR) end layout_file end def result # default XHTML header/footer @title = strip_html(compile_inline(@chapter.title)) @body = @output.string @language = @book.config['language'] @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 ReVIEW::Template.load(layoutfile).result(binding) end def xmlns_ops_prefix if @book.config['epubversion'].to_i == 3 'epub' else 'ops' end end def headline(level, label, caption) prefix, anchor = headline_prefix(level) if prefix prefix = %Q(#{prefix}) end puts '' if level > 1 a_id = '' if anchor a_id = %Q() end if caption.empty? puts a_id if label elsif label puts %Q(#{compile_inline(caption)}
) end blocked_lines = split_paragraph(lines) puts blocked_lines.join("\n") puts '#{compile_inline(caption)}
) end print %Q() lines.each do |line| puts detab(line) end puts '' puts '
#{lines.join}
) @noindent = nil else puts "#{lines.join}
" end end def parasep puts '#{I18n.t('list')}#{I18n.t('format_number_header', [get_chap, @chapter.list(id).number])}#{I18n.t('caption_prefix')}#{compile_inline(caption)}
) else puts %Q(#{I18n.t('list')}#{I18n.t('format_number_header_without_chapter', [@chapter.list(id).number])}#{I18n.t('caption_prefix')}#{compile_inline(caption)}
) end end def list_body(_id, lines, lang) class_names = ['list'] lexer = lang class_names.push("language-#{lexer}") unless lexer.blank? class_names.push('highlight') if highlight? print %Q() body = lines.inject('') { |i, j| i + detab(j) + "\n" } puts highlight(body: body, lexer: lexer, format: 'html') puts '' end def source(lines, caption = nil, lang = nil) puts %Q(
#{compile_inline(caption)}
) end end def source_body(_id, lines, lang) print %Q() body = lines.inject('') { |i, j| i + detab(j) + "\n" } lexer = lang puts highlight(body: body, lexer: lexer, format: 'html') puts '' end def listnum(lines, id, caption, lang = nil) puts %Q(
) first_line_num = line_num lines.each_with_index do |line, i| puts detab((i + first_line_num).to_s.rjust(2) + ': ' + line) end puts '' end end def emlist(lines, caption = nil, lang = nil) puts %Q(
#{compile_inline(caption)}
) end class_names = ['emlist'] class_names.push("language-#{lang}") unless lang.blank? class_names.push('highlight') if highlight? print %Q() body = lines.inject('') { |i, j| i + detab(j) + "\n" } lexer = lang puts highlight(body: body, lexer: lexer, format: 'html') puts '' puts '
#{compile_inline(caption)}
) end if highlight? body = lines.inject('') { |i, j| i + detab(j) + "\n" } lexer = lang first_line_number = line_num puts highlight(body: body, lexer: lexer, format: 'html', linenum: true, options: { linenostart: first_line_number }) else class_names = ['emlist'] class_names.push("language-#{lang}") unless lang.blank? class_names.push('highlight') if highlight? print %Q() first_line_num = line_num lines.each_with_index do |line, i| puts detab((i + first_line_num).to_s.rjust(2) + ': ' + line) end puts '' end puts '
#{compile_inline(caption)}
) end print %Q() body = lines.inject('') { |i, j| i + detab(j) + "\n" } lexer = 'shell-session' puts highlight(body: body, lexer: lexer, format: 'html') puts '' puts '
' end private :quotedlist def quote(lines) blocked_lines = split_paragraph(lines) puts %Q() lines.each do |line| puts detab(line) end puts '
#{blocked_lines.join("\n")}) end def doorquote(lines, ref) blocked_lines = split_paragraph(lines) puts %Q(
) puts blocked_lines.join("\n") puts %Q(' end def talk(lines) puts %Q(#{ref}より
) puts '
#{I18n.t('equation')}#{I18n.t('format_number_header', [get_chap, @chapter.equation(id).number])}#{I18n.t('caption_prefix')}#{compile_inline(caption)}
) else puts %Q(#{I18n.t('equation')}#{I18n.t('format_number_header_without_chapter', [@chapter.equation(id).number])}#{I18n.t('caption_prefix')}#{compile_inline(caption)}
) end end def texequation_body(lines) puts %Q(' puts escape(lines.join("\n")) puts '' end puts '
) lines.each do |line| puts detab(line) end puts '' image_header id, caption puts '
) if get_chap puts %Q(#{I18n.t('image')}#{I18n.t('format_number_header', [get_chap, @chapter.image(id).number])}#{I18n.t('caption_prefix')}#{compile_inline(caption)}) else puts %Q(#{I18n.t('image')}#{I18n.t('format_number_header_without_chapter', [@chapter.image(id).number])}#{I18n.t('caption_prefix')}#{compile_inline(caption)}) end puts '
' end def table(lines, id = nil, caption = nil) rows = [] sepidx = nil lines.each_with_index do |line, idx| if /\A[\=\-]{12}/ =~ line # just ignore # error "too many table separator" if sepidx sepidx ||= idx next end rows.push(line.strip.split(/\t+/).map { |s| s.sub(/\A\./, '') }) end rows = adjust_n_cols(rows) if id puts %Q(#{compile_inline(caption)}
) elsif get_chap puts %Q(#{I18n.t('table')}#{I18n.t('format_number_header', [get_chap, @chapter.table(id).number])}#{I18n.t('caption_prefix')}#{compile_inline(caption)}
) else puts %Q(#{I18n.t('table')}#{I18n.t('format_number_header_without_chapter', [@chapter.table(id).number])}#{I18n.t('caption_prefix')}#{compile_inline(caption)}
) end end def table_begin(_ncols) puts '#{str} | " end def td(str) "#{str} | " end def table_end puts '
---|
[*#{@chapter.footnote(id).number}] #{compile_inline(str)}
[*#{@chapter.footnote(id).number}] #{compile_inline(str)}
) lines.each do |line| puts detab(line) end puts '' end end if caption.present? puts %Q(
) puts %Q(#{I18n.t('numberless_image')}#{I18n.t('caption_prefix')}#{compile_inline(caption)}) puts '
' end puts '#{escape(str)}
)
else
%Q(#{escape(str)})
end
end
def inline_ttb(str)
if @book.htmlversion == 5
%Q(#{escape(str)}
)
else
%Q(#{escape(str)})
end
end
def inline_dtp(str)
""
end
def inline_code(str)
if @book.htmlversion == 5
%Q(#{escape(str)}
)
else
%Q(#{escape(str)})
end
end
def inline_idx(str)
%Q(#{escape(str)})
end
def inline_hidx(str)
%Q()
end
def inline_br(_str)
'#{escape(str)}
)
else
%Q(#{escape(str)})
end
end
def inline_del(str)
inline_asis(str, 'del')
end
def inline_ins(str)
inline_asis(str, 'ins')
end
def inline_u(str)
%Q(#{escape(str)})
end
def inline_recipe(str)
%Q(「#{escape(str)}」)
end
def inline_icon(id)
begin
%Q(missing image: #{id}) end end def inline_uchar(str) %Q(#{str};) end def inline_comment(str) if @book.config['draft'] %Q(#{escape(str)}) else '' end end def inline_tcy(str) # 縦中横用のtcy、uprightのCSSスタイルについては電書協ガイドラインを参照 style = 'tcy' if str.size == 1 && str.match(/[[:ascii:]]/) style = 'upright' end %Q(#{escape(str)}) end def inline_balloon(str) %Q(#{escape_html(str)}) end def inline_raw(str) super(str) end def nofunc_text(str) escape(str) end def compile_href(url, label) if @book.config['externallink'] %Q(#{label.nil? ? escape(url) : escape(label)}) else label.nil? ? escape(url) : I18n.t('external_link', [escape(label), escape(url)]) end end def flushright(lines) puts split_paragraph(lines).join("\n").gsub('
', %Q(
)) end def centering(lines) puts split_paragraph(lines).join("\n").gsub('
', %Q(
)) end def image_ext 'png' end def olnum(num) @ol_num = num.to_i end def defer_math_image(str, path, key) # for Re:VIEW >3 File.open(File.join(File.dirname(path), '__IMGMATH_BODY__.tex'), 'a+') do |f| f.puts str f.puts '\\clearpage' end File.open(File.join(File.dirname(path), '__IMGMATH_BODY__.map'), 'a+') do |f| f.puts key end end def make_math_image(str, path, fontsize = 12) # Re:VIEW 2 compatibility fontsize2 = (fontsize * 1.2).round.to_i texsrc = <<-EOB \\documentclass[12pt]{article} \\usepackage[utf8]{inputenc} \\usepackage{amsmath} \\usepackage{amsthm} \\usepackage{amssymb} \\usepackage{amsfonts} \\usepackage{anyfontsize} \\usepackage{bm} \\pagestyle{empty} \\begin{document} \\fontsize{#{fontsize}}{#{fontsize2}}\\selectfont #{str} \\end{document} EOB Dir.mktmpdir do |tmpdir| tex_path = File.join(tmpdir, 'tmpmath.tex') dvi_path = File.join(tmpdir, 'tmpmath.dvi') File.write(tex_path, texsrc) cmd = "latex --interaction=nonstopmode --output-directory=#{tmpdir} #{tex_path} && dvipng -T tight -z9 -o #{path} #{dvi_path}" out, status = Open3.capture2e(cmd) unless status.success? error "latex compile error\n\nError log:\n" + out end end end end end # module ReVIEW