testdata/mybook/lib/ruby/review-htmlbuilder.rb in review-retrovert-0.9.7 vs testdata/mybook/lib/ruby/review-htmlbuilder.rb in review-retrovert-0.9.8
- old
+ new
@@ -14,10 +14,14 @@
class HTMLBuilder
attr_accessor :starter_config
+ def target_name
+ "html"
+ end
+
def layoutfile
## 'rake web' のときに使うレイアウトファイルを 'layout.html5.erb' へ変更
if @book.config.maker == 'webmaker'
htmldir = 'web/html'
#localfilename = 'layout-web.html.erb'
@@ -57,11 +61,11 @@
if @book.config.maker == 'webmaker'
#@toc = ReVIEW::WEBTOCPrinter.book_to_string(@book) #-
@toc = ReVIEW::WEBTOCPrinter.book_to_html(@book, @chapter) #+
end
- ReVIEW::Template.load(layoutfile).result(binding)
+ ReVIEW::Template.load(layoutfile()).result(binding())
end
def headline(level, label, caption)
prefix, anchor = headline_prefix(level)
prefix = "<span class=\"secno\">#{prefix}</span> " if prefix
@@ -97,77 +101,124 @@
(attrs[k] ||= []) << v
end
attrs.map {|k, arr| " #{k}=\"#{arr.join(' ')}\"" }.join()
end
- def image_image(id, caption, metric)
- src = @chapter.image(id).path.sub(%r{\A\./}, '')
+ ## 画像
+
+ def _render_image(id, image_filepath, caption, opts)
+ src = image_filepath.sub(/\A\.\//, '')
alt = escape_html(compile_inline(caption))
- metrics = parse_metric('html', metric)
- metrics = " class=\"img\"" unless metrics.present?
+ #
+ classes = ["img"]
+ styles = []
+ #
+ if opts[:scale]
+ case (scale = opts[:scale])
+ when /\A\d+\z/, /\A\d\.\d*\z/, /\A\.\d+\z/
+ per = (scale.to_f * 100).round()
+ when /\A\d+(\.\d+)?%\z/
+ per = scale.sub(/%\z/, '').to_f.round()
+ else
+ error "scale=#{scale}: invalid scale value."
+ end
+ found = PERCENT_THRESHOLDS.find {|x| x == per }
+ if found
+ classes << 'width-%03dper' % per
+ else
+ styles << "max-width:#{per}%"
+ end
+ end
+ #
+ classes << opts[:class] if opts[:class]
+ classes << "border" if opts[:border]
+ classes << "draft" if opts[:draft]
+ styles << opts[:style] if opts[:style]
+ styles << "width:#{opts[:width]}" if opts[:width]
+ #
puts "<div id=\"#{normalize_id(id)}\" class=\"image\">"
- puts "<img src=\"#{src}\" alt=\"#{alt}\"#{metrics} />"
+ print "<img src=\"#{src}\" alt=\"#{alt}\""
+ print " class=\"#{classes.join(' ')}\"" unless classes.empty?
+ print " style=\"#{styles.join(' ')}\"" unless styles.empty?
+ puts " />"
image_header(id, caption)
puts "</div>"
end
- ## コードブロック(//program, //terminal)
+ PERCENT_THRESHOLDS = [
+ 10, 20, 25, 30, 33, 40, 50, 60, 67, 70, 75, 80, 90, 100,
+ ]
- def program(lines, id=nil, caption=nil, optionstr=nil)
- _codeblock('program', 'code', lines, id, caption, optionstr)
+ protected
+
+ ## コードブロック(//program, //terminal, //output)
+
+ CLASSNAMES = {
+ "program" => "code",
+ "terminal" => "cmd-code",
+ "list" => "caption-code",
+ "listnum" => "code",
+ "emlist" => "emlist-code",
+ "emlistnum" => "emlistnum-code",
+ "source" => "source-code",
+ "cmd" => "cmd-code",
+ "output" => "output",
+ }
+
+ def _codeblock_eolmark()
+ "<small class=\"startereolmark\"></small>"
end
- def terminal(lines, id=nil, caption=nil, optionstr=nil)
- _codeblock('terminal', 'cmd-code', lines, id, caption, optionstr)
+ def _codeblock_indentmark()
+ '<span class="indentbar"> </span>'
end
- protected
-
- def _codeblock(blockname, classname, lines, id, caption, optionstr)
- ## ブロックコマンドのオプション引数はCompilerクラスでパースすべき。
- ## しかしCompilerクラスがそのような設計になってないので、
- ## 仕方ないのでBuilderクラスでパースする。
- opts = _parse_codeblock_optionstr(optionstr, blockname)
- CODEBLOCK_OPTIONS.each {|k, v| opts[k] = v unless opts.key?(k) }
+ def _render_codeblock(blockname, lines, id, caption_str, opts)
+ caption = caption_str
#
if opts['eolmark']
- lines = lines.map {|line| "#{detab(line)}<small class=\"startereolmark\"></small>" }
+ eolmark = _codeblock_eolmark() # ex: '{\startereolmark}'
else
- lines = lines.map {|line| detab(line) }
+ eolmark = nil
end
#
- indent_w = opts['indentwidth']
- if indent_w && indent_w > 0
- indent_str = " " * (indent_w - 1) + '<span class="indentbar"> </span>'
- lines = lines.map {|line|
- line.sub(/\A( +)/) {
- m, n = ($1.length - 1).divmod indent_w
- " " << indent_str * m << " " * n
- }
- }
+ eol = eolmark ? "#{eolmark}\n" : "\n"
+ lines = lines.map {|line|
+ line = _parse_inline(line) {|text| escape(text) }
+ }
+ #
+ indent_width = opts['indent']
+ if indent_width && indent_width > 0
+ lines = _add_indent_mark(lines, indent_width)
end
#
+ classname = CLASSNAMES[blockname] || "code"
+ classname += " #{opts['classname']}" if opts['classname']
puts "<div id=\"#{normalize_id(id)}\" class=\"#{classname}\">" if id.present?
puts "<div class=\"#{classname}\">" unless id.present?
#
+ classattr = nil
if id.present? || caption.present?
str = _build_caption_str(id, caption)
print "<span class=\"caption\">#{str}</span>\n"
classattr = "list"
+ end
+ if blockname == "output"
+ classattr = blockname
else
- classattr = "emlist"
+ classattr ||= "emlist"
end
#
lang = opts['lang']
lang = File.extname(id || "").gsub(".", "") if lang.blank?
classattr << " language-#{lang}" unless lang.blank?
classattr << " highlight" if highlight?
print "<pre class=\"#{classattr}\">"
#
gen = opts['lineno'] ? LineNumberGenerator.new(opts['lineno']).each : nil
if gen
- width = opts['linenowidth']
+ width = opts['linenowidth'] || -1
if width < 0
format = "%s"
elsif width == 0
last_lineno = gen.each.take(lines.length).compact.last
width = last_lineno.to_s.length
@@ -175,45 +226,61 @@
else
format = "%#{width}s"
end
end
buf = []
- start_tag = opts['linenowidth'] >= 0 ? "<em class=\"linenowidth\">" : "<em class=\"lineno\">"
+ start_tag = (opts['linenowidth'] || -1) >= 0 ? "<em class=\"linenowidth\">" : "<em class=\"lineno\">"
lines.each_with_index do |line, i|
buf << start_tag << (format % gen.next) << ": </em>" if gen
- buf << line << "\n"
+ buf << line #<< "\n"
end
puts highlight(body: buf.join(), lexer: lang,
format: "html", linenum: !!gen,
#options: {linenostart: start}
)
#
print "</pre>\n"
print "</div>\n"
end
+ def _add_indent_mark(lines, indent_width)
+ space = " "
+ rexp = /\A( +)/
+ #
+ width = indent_width
+ mark = _codeblock_indentmark() # ex: '{\starterindentmark}'
+ indent = space * (width - 1) + mark
+ nchar = space.length
+ return lines.map {|line|
+ line.sub(rexp) {
+ m, n = ($1.length / nchar - 1).divmod width
+ "#{space}#{indent * m}#{space * n}"
+ }
+ }
+ end
+
public
## コードリスト(//list, //emlist, //listnum, //emlistnum, //cmd, //source)
def list(lines, id=nil, caption=nil, lang=nil)
- _codeblock("list", "caption-code", lines, id, caption, _codeblock_optstr(lang, false))
+ _codeblock("list", lines, id, caption, _codeblock_optstr(lang, false))
end
def listnum(lines, id=nil, caption=nil, lang=nil)
- _codeblock("listnum", "code", lines, id, caption, _codeblock_optstr(lang, true))
+ _codeblock("listnum", lines, id, caption, _codeblock_optstr(lang, true))
end
def emlist(lines, caption=nil, lang=nil)
- _codeblock("emlist", "emlist-code", lines, nil, caption, _codeblock_optstr(lang, false))
+ _codeblock("emlist", lines, nil, caption, _codeblock_optstr(lang, false))
end
def emlistnum(lines, caption=nil, lang=nil)
- _codeblock("emlistnum", "emlistnum-code", lines, nil, caption, _codeblock_optstr(lang, true))
+ _codeblock("emlistnum", lines, nil, caption, _codeblock_optstr(lang, true))
end
def source(lines, caption=nil, lang=nil)
- _codeblock("source", "source-code", lines, nil, caption, _codeblock_optstr(lang, false))
+ _codeblock("source", lines, nil, caption, _codeblock_optstr(lang, false))
end
def cmd(lines, caption=nil, lang=nil)
lang ||= "shell-session"
- _codeblock("cmd", "cmd-code", lines, nil, caption, _codeblock_optstr(lang, false))
+ _codeblock("cmd", lines, nil, caption, _codeblock_optstr(lang, false))
end
def _codeblock_optstr(lang, lineno_flag)
arr = []
arr << lang if lang
if lineno_flag
@@ -293,23 +360,20 @@
def ol_item_end()
puts "</li>"
end
- ## 入れ子可能なブロック命令
+ def dl_dd_begin()
+ puts "<dd>"
+ end
- def on_minicolumn(type, caption, &b)
- puts "<div class=\"#{type}\">"
- puts "<p class=\"caption\">#{compile_inline(caption)}</p>" if caption.present?
- yield
- puts '</div>'
+ def dl_dd_end()
+ puts "</dd>"
end
- protected :on_minicolumn
- def on_sideimage_block(imagefile, imagewidth, option_str=nil, &b)
- imagefile, imagewidth, opts = validate_sideimage_args(imagefile, imagewidth, option_str)
- filepath = find_image_filepath(imagefile)
+ ## 画像横に文章
+ def _render_sideimage(filepath, imagewidth, opts, &b)
side = (opts['side'] || 'L') == 'L' ? 'left' : 'right'
imgclass = opts['border'] ? "image-bordered" : nil
normalize = proc {|s| s =~ /^\A(\d+(\.\d+))%\z/ ? "#{$1.to_f/100.0}\\textwidth" : s }
imgwidth = normalize.call(imagewidth)
boxwidth = normalize.call(opts['boxwidth']) || imgwidth
@@ -325,10 +389,22 @@
puts " </div>\n"
puts " </div>\n"
puts "</div>\n"
end
+ ## 入れ子可能なブロック命令
+
+ def on_minicolumn(type, caption=nil, &b)
+ with_context(:minicolumn) do
+ puts "<div class=\"miniblock miniblock-#{type}\">"
+ puts "<p class=\"miniblock-caption\">#{compile_inline(caption)}</p>" if caption.present?
+ yield
+ puts '</div>'
+ end
+ end
+ protected :on_minicolumn
+
#### ブロック命令
def footnote(id, str)
id_val = "fn-#{normalize_id(id)}"
href = "#fnb-#{normalize_id(id)}"
@@ -383,26 +459,95 @@
#
__original_texequation(lines)
end
## 章 (Chapter) の概要
- def abstract(lines)
+ def on_abstract_block()
puts '<div class="abstract">'
- puts lines
+ yield
puts '</div>'
end
+ ## 章 (Chapter) の著者
+ def on_chapterauthor_block(name)
+ puts "<div class=\"chapterauthor\">#{escape(name)}</div>"
+ end
+
+ ## 会話リスト
+ def _render_talklist(opts, &b)
+ c = opts[:classname]
+ s = c ? " #{c}" : nil
+ puts "<div class=\"talk-outer#{s}\">"
+ puts " <ul class=\"talk-list#{s}\">"
+ yield
+ puts " </ul>"
+ puts "</div>"
+ end
+
+ ## 会話項目
+ def _render_talk(image_filepath=nil, name=nil, &b)
+ puts "<li class=\"talk-item\">"
+ print " <span class=\"talk-chara\">"
+ if image_filepath.present?
+ print "<img class=\"talk-image\" src=\"#{image_filepath}\"/>"
+ else
+ s = compile_inline(name || '')
+ print "<b class=\"talk-name\">#{s}</b>"
+ end
+ puts "</span>"
+ print " <div class=\"talk-text\">"
+ yield
+ if @output.string.end_with?("\n")
+ @output.seek(-1, IO::SEEK_CUR) # 改行文字を取り除く
+ #@output.string.chomp! # これだとヌル文字が入ってしまう
+ end
+ puts "</div>"
+ puts "</li>"
+ end
+
+ ## キーと説明文のリスト
+ def _render_desclist(opts, &b)
+ bkup = @_desclist_opts
+ @_desclist_opts = opts
+ classnames = ['desc-list']
+ classnames << 'compact' if opts[:compact]
+ classnames << 'item-bold' if opts[:bold]
+ classnames << opts[:classname] if opts[:classname]
+ puts "<dl class=\"#{classnames.join(' ')}\">"
+ yield
+ puts "</dl>"
+ @_desclist_opts = bkup
+ end
+
+ ## キーと説明文
+ def _render_desc(key, &b)
+ opts = @_desclist_opts
+ s = compile_inline(key)
+ s = "<b>#{s}</b>" if opts[:bold]
+ puts " <dt class=\"desc\">#{s}</dt>"
+ puts " <dd class=\"desc\">"
+ yield
+ puts " </dd>"
+ end
+
+ ## 縦方向の空きを入れる
+ def _render_vspace(size)
+ puts "<div class=\"vspace\" style=\"height:#{size}\"></div>"
+ end
+
+ def _render_addvspace(size)
+ puts "<div class=\"addvspace\" style=\"height:#{size}\"></div>"
+ end
+
## 章タイトルを独立したページに
def makechaptitlepage(option=nil)
puts '' # HTMLでは特に何もしない
end
## 縦方向のスペースがなければ改ページ
- def needvspace(builder_name, height)
- if builder_name == 'html' || builder_name == 'epub'
- puts "<div style=\"height:#{height}\"></div>"
- end
+ def _render_needvspace(height)
+ puts "<div style=\"height:#{height}\"></div>"
end
## 段(Paragraph)の終わりにスペースを入れる
def paragraphend()
puts '' # HTMLでは特に何もしない
@@ -443,19 +588,21 @@
end
## ノート(//note{ ... //})
## (入れ子対応なので、中に箇条書きや別のブロックを入れられる)
def on_note_block(label=nil, caption=nil)
- caption, label = label, nil if caption.nil?
- if label
- puts "<div class=\"note\" id=\"#{label}\">"
- else
- puts "<div class=\"note\">"
+ with_context(:minicolumn) do
+ caption, label = label, nil if caption.nil?
+ if label
+ puts "<div class=\"note\" id=\"#{label}\">"
+ else
+ puts "<div class=\"note\">"
+ end
+ puts "<h5>#{compile_inline(caption)}</h5>" if caption.present?
+ yield
+ puts "</div>"
end
- puts "<h5>#{compile_inline(caption)}</h5>" if caption.present?
- yield
- puts "</div>"
end
def note(lines, label=nil, caption=nil)
on_quote_block(label, caption) do
puts lines
end
@@ -470,10 +617,93 @@
end
def note_end(level)
puts "</div>"
end
+ ## テーブル
+ def table(lines, id=nil, caption=nil, option=nil)
+ super
+ end
+
+ def table_header(id, caption, options)
+ if id.present?
+ begin
+ num = @chapter.table(id).number
+ rescue KeyError
+ error "no such table: #{id}"
+ end
+ s1 = I18n.t('table')
+ s2 = get_chap() \
+ ? I18n.t('format_number_header', [get_chap(), num]) \
+ : I18n.t('format_number_header_without_chapter', [num])
+ s3 = I18n.t('caption_prefix')
+ puts "<p class=\"caption\">#{s1}#{s2}#{s3}#{compile_inline(caption||'')}</p>"
+ elsif caption.present?
+ puts "<p class=\"caption\">#{compile_inline(caption||'')}</p>"
+ end
+ end
+
+ def table_begin(_ncols, fontsize: nil)
+ if fontsize
+ puts "<table style=\"font-size:#{fontsize}\">"
+ else
+ puts "<table>"
+ end
+ end
+
+ protected
+
+ def _table_before(id, caption, opts)
+ class_ = opts[:csv] ? "table table-nohline" : "table"
+ if id.present?
+ puts "<div id=\"#{normalize_id(id)}\" class=\"#{class_}\">"
+ else
+ puts "<div class=\"#{class_}\">"
+ end
+ end
+
+ def _table_after(id, caption, opts)
+ puts "</div>"
+ end
+
+ def _table_bottom(hline: false)
+ end
+
+ def _table_tr(cells, hline: false)
+ if hline
+ "<tr class=\"hline\">#{cells.join}</tr>"
+ else
+ "<tr>#{cells.join}</tr>"
+ end
+ end
+
+ public
+
+ ## //imgtable
+ def imgtable(lines, id, caption=nil, option=nil)
+ super
+ end
+
+ protected
+
+ def _render_imgtable(id, caption, opts)
+ puts "<div id=\"#{normalize_id(id)}\" class=\"imgtable image\">"
+ table_header(id, caption, opts)
+ puts " <div>"
+ yield
+ puts " </div>"
+ puts "</div>"
+ end
+
+ def _render_imgtable_caption(caption)
+ end
+
+ def _render_imgtable_label(id)
+ end
+
+ public
+
#### インライン命令
def inline_fn(id)
if @book.config['epubversion'].to_i == 3
type = " epub:type=\"noteref\""
@@ -483,10 +713,20 @@
return "<sup><a id=\"fnb-#{normalize_id(id)}\" href=\"#fn-#{normalize_id(id)}\" class=\"noteref\"#{type}>*#{@chapter.footnote(id).number}</a></sup>"
rescue KeyError
error "unknown footnote: #{id}"
end
+ ## 改段落(箇条書き内では空行を入れられないため)
+ def inline_par(arg)
+ case arg
+ when 'i'
+ "<p class=\"indent\">"
+ else
+ "<p>"
+ end
+ end
+
## ファイル名
def inline_file(str)
on_inline_file { escape(str) }
end
def on_inline_file
@@ -550,30 +790,40 @@
## nestable inline commands
def on_inline_i() ; "<i>#{yield}</i>" ; end
def on_inline_b() ; "<b>#{yield}</b>" ; end
- def on_inline_code() ; "<code>#{yield}</code>" ; end
+ #def on_inline_code() ; "<code>#{yield}</code>" ; end
def on_inline_tt() ; "<tt>#{yield}</tt>" ; end
def on_inline_del() ; "<s>#{yield}</s>" ; end
def on_inline_sub() ; "<sub>#{yield}</sub>" ; end
def on_inline_sup() ; "<sup>#{yield}</sup>" ; end
def on_inline_em() ; "<em>#{yield}</em>" ; end
def on_inline_strong(); "<strong>#{yield}</strong>" ; end
def on_inline_u() ; "<u>#{yield}</u>" ; end
def on_inline_ami() ; "<span class=\"ami\">#{yield}</span>"; end
def on_inline_balloon(); "<span class=\"balloon\">← #{yield}</span>"; end
+ def on_inline_code()
+ return "<code class=\"inline-code\">#{yield}</code>"
+ end
+
+ ## 「“」と「”」で囲む
+ def on_inline_qq()
+ "“#{yield}”"
+ end
+
def build_inline_href(url, escaped_label) # compile_href()をベースに改造
flag_link = @book.config['externallink']
return _inline_hyperlink(url, escaped_label, flag_link)
end
def inline_hlink(str)
url, label = str.split(/, /, 2)
flag_link = @book.config['externallink']
- return _inline_hyperlink(url, escape(label), flag_link)
+ label_ = label.present? ? escape(label) : nil
+ return _inline_hyperlink(url, label_, flag_link)
end
def _inline_hyperlink(url, escaped_label, flag_link)
if flag_link
label = escaped_label || escape_html(url)
@@ -607,13 +857,51 @@
def build_eq(chapter, label, number)
s = "#{I18n.t('equation')}#{chapter.number}.#{number}"
"<a>#{escape(s)}</a>"
end
+ ## 索引に載せる語句 (@<idx>{}, @<term>{})
+ public
+ def inline_idx(str)
+ s1, s2 = _compile_term(str)
+ %Q`<span class="index" title="#{s2}">#{s1}</span>`
+ end
+ def inline_hidx(str)
+ _, s2 = _compile_term(str)
+ %Q`<span class="index" title="#{s2}"></span>`
+ end
+ def inline_term(str)
+ s1, s2 = _compile_term(str)
+ %Q`<span class="index term" title="#{s2}">#{s1}</span>`
+ end
+ def on_inline_termnoidx(str)
+ %Q`<span class="term">#{yield}</span>`
+ end
+
+ def _compile_term(str)
+ arr = []
+ placeholder = "---"
+ display_str, see = parse_term(str, placeholder) do |term, term_e, yomi|
+ if yomi
+ arr << escape(yomi)
+ else
+ arr << term_e
+ end
+ end
+ title_attr = arr.join()
+ title_attr += " → see: #{escape(see)}" if see
+ return display_str, title_attr
+ end
+ private :_compile_term
+
end
class TEXTBuilder
+
+ def target_name
+ "text"
+ end
## nestable inline commands
def on_inline_i() ; "<i>#{yield}</i>" ; end
def on_inline_b() ; "<b>#{yield}</b>" ; end