# Copyright (c) 2008-2019 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(#{a_id}#{prefix}#{compile_inline(caption)}) else puts %Q(#{a_id}#{prefix}#{compile_inline(caption)}) end end def nonum_begin(level, label, caption) @nonum_counter += 1 puts if level > 1 return unless caption.present? if label puts %Q(#{compile_inline(caption)}) else id = normalize_id("#{@chapter.name}_nonum#{@nonum_counter}") puts %Q(#{compile_inline(caption)}) end end def nonum_end(level) end def notoc_begin(level, label, caption) @nonum_counter += 1 puts if level > 1 return unless caption.present? if label puts %Q(#{compile_inline(caption)}) else id = normalize_id("#{@chapter.name}_nonum#{@nonum_counter}") puts %Q(#{compile_inline(caption)}) end end def notoc_end(level) end def nodisp_begin(level, label, caption) @nonum_counter += 1 puts '' if level > 1 return unless caption.present? if label puts %Q() else id = normalize_id("#{@chapter.name}_nonum#{@nonum_counter}") puts %Q() end end def nodisp_end(level) end def column_begin(level, label, caption) puts %Q(
) @column += 1 puts if level > 1 a_id = %Q() if caption.empty? puts a_id if label elsif label puts %Q(#{a_id}#{compile_inline(caption)}) else puts %Q(#{a_id}#{compile_inline(caption)}) end end def column_end(_level) puts '
' end def xcolumn_begin(level, label, caption) puts %Q(
) headline(level, label, caption) end def xcolumn_end(_level) puts '
' end def ref_begin(level, label, caption) print %Q(
) headline(level, label, caption) end def ref_end(_level) puts '
' end def sup_begin(level, label, caption) print %Q(
) headline(level, label, caption) end def sup_end(_level) puts '
' end def captionblock(type, lines, caption) puts %Q(
) if caption.present? puts %Q(

#{compile_inline(caption)}

) end blocked_lines = split_paragraph(lines) puts blocked_lines.join("\n") puts '
' end def memo(lines, caption = nil) captionblock('memo', lines, caption) end def tip(lines, caption = nil) captionblock('tip', lines, caption) end def info(lines, caption = nil) captionblock('info', lines, caption) end def planning(lines, caption = nil) captionblock('planning', lines, caption) end def best(lines, caption = nil) captionblock('best', lines, caption) end def important(lines, caption = nil) captionblock('important', lines, caption) end def security(lines, caption = nil) captionblock('security', lines, caption) end def caution(lines, caption = nil) captionblock('caution', lines, caption) end def notice(lines, caption = nil) captionblock('notice', lines, caption) end def warning(lines, caption = nil) captionblock('warning', lines, caption) end def point(lines, caption = nil) captionblock('point', lines, caption) end def shoot(lines, caption = nil) captionblock('shoot', lines, caption) end def box(lines, caption = nil) puts %Q(
) if caption.present? puts %Q(

#{compile_inline(caption)}

) end print %Q(
)
      lines.each do |line|
        puts detab(line)
      end
      puts '
' puts '
' end def note(lines, caption = nil) captionblock('note', lines, caption) end def ul_begin puts '' end def ol_begin if @ol_num puts %Q(
    ) # it's OK in HTML5, but not OK in XHTML1.1 @ol_num = nil else puts '
      ' end end def ol_item(lines, _num) puts "
    1. #{lines.join}
    2. " end def ol_end puts '
    ' end def dl_begin puts '
    ' end def dt(line) puts "
    #{line}
    " end def dd(lines) puts "
    #{lines.join}
    " end def dl_end puts '
    ' end def paragraph(lines) if @noindent puts %Q(

    #{lines.join}

    ) @noindent = nil else puts "

    #{lines.join}

    " end end def parasep puts '
    ' end def read(lines) blocked_lines = split_paragraph(lines) puts %Q(
    \n#{blocked_lines.join("\n")}\n
    ) end alias_method :lead, :read def list(lines, id, caption, lang = nil) puts %Q(
    ) begin list_header id, caption, lang rescue KeyError error "no such list: #{id}" end list_body id, lines, lang puts '
    ' end def list_header(id, caption, _lang) if get_chap puts %Q(

    #{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(
    ) source_header caption source_body caption, lines, lang puts '
    ' end def source_header(caption) if caption.present? 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(
    ) begin list_header id, caption, lang rescue KeyError error "no such list: #{id}" end listnum_body lines, lang puts '
    ' end def listnum_body(lines, lang) body = lines.inject('') { |i, j| i + detab(j) + "\n" } lexer = lang first_line_number = line_num hs = highlight(body: body, lexer: lexer, format: 'html', linenum: true, options: { linenostart: first_line_number }) if highlight? puts hs else class_names = ['list'] class_names.push("language-#{lang}") unless lang.blank? print %Q(
    )
            hs.split("\n").each_with_index do |line, i|
              puts detab((i + first_line_number).to_s.rjust(2) + ': ' + line)
            end
            puts '
    ' end end def emlist(lines, caption = nil, lang = nil) puts %Q(
    ) if caption.present? 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 '
    ' end def emlistnum(lines, caption = nil, lang = nil) puts %Q(
    ) if caption.present? puts %Q(

    #{compile_inline(caption)}

    ) end body = lines.inject('') { |i, j| i + detab(j) + "\n" } lexer = lang first_line_number = line_num hs = highlight(body: body, lexer: lexer, format: 'html', linenum: true, options: { linenostart: first_line_number }) if highlight? puts hs else class_names = ['emlist'] class_names.push("language-#{lang}") unless lang.blank? class_names.push('highlight') if highlight? print %Q(
    )
            hs.split("\n").each_with_index do |line, i|
              puts detab((i + first_line_number).to_s.rjust(2) + ': ' + line)
            end
            puts '
    ' end puts '
    ' end def cmd(lines, caption = nil) puts %Q(
    ) if caption.present? puts %Q(

    #{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 def quotedlist(lines, css_class) print %Q(
    )
          lines.each do |line|
            puts detab(line)
          end
          puts '
    ' end private :quotedlist def quote(lines) blocked_lines = split_paragraph(lines) puts %Q(
    #{blocked_lines.join("\n")}
    ) end def doorquote(lines, ref) blocked_lines = split_paragraph(lines) puts %Q(
    ) puts blocked_lines.join("\n") puts %Q(

    #{ref}より

    ) puts '
    ' end def talk(lines) puts %Q(
    ) blocked_lines = split_paragraph(lines) puts blocked_lines.join("\n") puts '
    ' end def texequation(lines, id = nil, caption = '') if id texequation_header id, caption end texequation_body(lines) if id puts '' end end def texequation_header(id, caption) puts %Q(
    ) if get_chap puts %Q(

    #{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(
    ) if @book.config['mathml'] require 'math_ml' require 'math_ml/symbol/character_reference' p = MathML::LaTeX::Parser.new(symbol: MathML::Symbol::CharacterReference) puts p.parse(unescape(lines.join("\n")), true) elsif @book.config['imgmath'] fontsize = @book.config['imgmath_options']['fontsize'].to_f lineheight = @book.config['imgmath_options']['lineheight'].to_f math_str = "\\begin{equation*}\n\\fontsize{#{fontsize}}{#{lineheight}}\\selectfont\n#{unescape(lines.join("\n"))}\n\\end{equation*}\n" key = Digest::SHA256.hexdigest(math_str) math_dir = File.join(@book.config['imagedir'], '_review_math') Dir.mkdir(math_dir) unless Dir.exist?(math_dir) img_path = File.join(math_dir, "_gen_#{key}.#{@book.config['imgmath_options']['format']}") if @book.config.check_version('2', exception: false) make_math_image(math_str, img_path) puts %Q() else defer_math_image(math_str, img_path, key) puts %Q(#{escape(lines.join(' '))}) end else print '
    '
            puts escape(lines.join("\n"))
            puts '
    ' end puts '
    ' end def handle_metric(str) if str =~ /\Ascale=([\d.]+)\Z/ return { 'class' => sprintf('width-%03dper', ($1.to_f * 100).round) } end k, v = str.split('=', 2) { k => v.sub(/\A["']/, '').sub(/["']\Z/, '') } end def result_metric(array) attrs = {} array.each do |item| k = item.keys[0] if attrs[k] attrs[k] << item[k] else attrs[k] = [item[k]] end end ' ' + attrs.map { |k, v| %Q(#{k}="#{v.join(' ')}") }.join(' ') end def image_image(id, caption, metric) metrics = parse_metric('html', metric) puts %Q(
    ) puts %Q(#{escape(compile_inline(caption))}) image_header id, caption puts '
    ' end def image_dummy(id, caption, lines) warn "image not bound: #{id}" puts %Q(
    ) puts %Q(
    )
          lines.each do |line|
            puts detab(line)
          end
          puts '
    ' image_header id, caption puts '
    ' end def image_header(id, caption) puts %Q(

    ) 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) error 'no rows in the table' if rows.empty? if id puts %Q(
    ) else puts %Q(
    ) end begin if caption.present? table_header id, caption end rescue KeyError error "no such table: #{id}" end table_begin rows.first.size if sepidx sepidx.times do tr(rows.shift.map { |s| th(s) }) end rows.each do |cols| tr(cols.map { |s| td(s) }) end else rows.each do |cols| h, *cs = *cols tr([th(h)] + cs.map { |s| td(s) }) end end table_end puts '
    ' end def table_header(id, caption) if id.nil? 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 '' end def tr(rows) puts "#{rows.join}" end def th(str) "" end def td(str) "" end def table_end puts '
    #{str}#{str}
    ' end def imgtable(lines, id, caption = nil, metric = nil) unless @chapter.image(id).bound? warn "image not bound: #{id}" image_dummy id, caption, lines return end puts %Q(
    ) begin if caption.present? table_header id, caption end rescue KeyError error "no such table: #{id}" end imgtable_image(id, caption, metric) puts '
    ' end def imgtable_image(id, caption, metric) metrics = parse_metric('html', metric) puts %Q(#{escape(compile_inline(caption))}) end def emtable(lines, caption = nil) table(lines, nil, caption) end def comment(lines, comment = nil) return unless @book.config['draft'] lines ||= [] lines.unshift escape(comment) unless comment.blank? str = lines.join('
    ') puts %Q(
    #{str}
    ) end def footnote(id, str) if @book.config['epubversion'].to_i == 3 back = '' if @book.config['epubmaker'] && @book.config['epubmaker']['back_footnote'] back = %Q(#{I18n.t('html_footnote_backmark')}) end # XXX: back link must be located at first of p for Kindle. puts %Q(

    #{back}#{I18n.t('html_footnote_textmark', @chapter.footnote(id).number)}#{compile_inline(str)}

    ) else puts %Q(

    [*#{@chapter.footnote(id).number}] #{compile_inline(str)}

    ) end end def indepimage(lines, id, caption = '', metric = nil) metrics = parse_metric('html', metric) caption = '' unless caption.present? puts %Q(
    ) begin puts %Q(#{escape(compile_inline(caption))}) rescue warn "image not bound: #{id}" if lines puts %Q(
    )
              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 '
    ' end alias_method :numberlessimage, :indepimage def hr puts '
    ' end def label(id) puts %Q() end def blankline puts '


    ' end def pagebreak puts %Q(
    ) end def bpo(lines) puts '' lines.each do |line| puts detab(line) end puts '' end def noindent @noindent = true end def inline_labelref(idref) %Q(「#{I18n.t('label_marker')}#{escape(idref)}」) end alias_method :inline_ref, :inline_labelref def inline_pageref(id) error "pageref op is unsupported on this builder: #{id}" end def inline_chapref(id) title = super if @book.config['chapterlink'] %Q(#{title}) else title end rescue KeyError error "unknown chapter: #{id}" end def inline_chap(id) if @book.config['chapterlink'] %Q(#{@book.chapter_index.number(id)}) else @book.chapter_index.number(id) end rescue KeyError error "unknown chapter: #{id}" end def inline_title(id) title = super if @book.config['chapterlink'] %Q(#{title}) else title end rescue KeyError error "unknown chapter: #{id}" end def inline_fn(id) if @book.config['epubversion'].to_i == 3 %Q(#{I18n.t('html_footnote_refmark', @chapter.footnote(id).number)}) else %Q(*#{@chapter.footnote(id).number}) end rescue KeyError error "unknown footnote: #{id}" end def compile_ruby(base, ruby) if @book.htmlversion == 5 %Q(#{escape(base)}#{I18n.t('ruby_prefix')}#{escape(ruby)}#{I18n.t('ruby_postfix')}) else %Q(#{escape(base)}#{I18n.t('ruby_prefix')}#{ruby}#{I18n.t('ruby_postfix')}) end end def compile_kw(word, alt) %Q() + if alt then escape(word + " (#{alt.strip})") else escape(word) end + "" end def inline_i(str) %Q(#{escape(str)}) end def inline_b(str) %Q(#{escape(str)}) end def inline_ami(str) %Q(#{escape(str)}) end def inline_bou(str) %Q(#{escape(str)}) end def inline_tti(str) if @book.htmlversion == 5 %Q(#{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) '
    ' end def inline_m(str) if @book.config['mathml'] require 'math_ml' require 'math_ml/symbol/character_reference' parser = MathML::LaTeX::Parser.new(symbol: MathML::Symbol::CharacterReference) %Q(#{parser.parse(str, nil)}) elsif @book.config['imgmath'] math_str = '$' + str + '$' key = Digest::SHA256.hexdigest(str) math_dir = File.join(@book.config['imagedir'], '_review_math') Dir.mkdir(math_dir) unless Dir.exist?(math_dir) img_path = File.join(math_dir, "_gen_#{key}.#{@book.config['imgmath_options']['format']}") if @book.config.check_version('2', exception: false) make_math_image(math_str, img_path) %Q() else defer_math_image(math_str, img_path, key) %Q(#{escape(str)}) end else %Q(#{escape(str)}) end end def text(str) str end def bibpaper(lines, id, caption) puts %Q(
    ) bibpaper_header id, caption bibpaper_bibpaper id, caption, lines unless lines.empty? puts '
    ' end def bibpaper_header(id, caption) print %Q() print "[#{@chapter.bibpaper(id).number}]" print '' puts " #{compile_inline(caption)}" end def bibpaper_bibpaper(_id, _caption, lines) print split_paragraph(lines).join end def inline_bib(id) %Q([#{@chapter.bibpaper(id).number}]) rescue KeyError error "unknown bib: #{id}" end def inline_hd_chap(chap, id) n = chap.headline_index.number(id) if n.present? && chap.number && over_secnolevel?(n) str = I18n.t('hd_quote', [n, compile_inline(chap.headline(id).caption)]) else str = I18n.t('hd_quote_without_number', compile_inline(chap.headline(id).caption)) end if @book.config['chapterlink'] anchor = 'h' + n.gsub('.', '-') %Q(#{str}) else str end rescue KeyError error "unknown headline: #{id}" end def column_label(id, chapter = @chapter) num = chapter.column(id).number "column-#{num}" end private :column_label def inline_column_chap(chapter, id) if @book.config['chapterlink'] %Q(#{I18n.t('column', compile_inline(chapter.column(id).caption))}) else I18n.t('column', compile_inline(chapter.column(id).caption)) end rescue KeyError error "unknown column: #{id}" end def inline_list(id) str = super(id) chapter, id = extract_chapter_id(id) if @book.config['chapterlink'] %Q(#{str}) else %Q(#{str}) end end def inline_table(id) str = super(id) chapter, id = extract_chapter_id(id) if @book.config['chapterlink'] %Q(#{str}) else %Q(#{str}) end end def inline_img(id) str = super(id) chapter, id = extract_chapter_id(id) if @book.config['chapterlink'] %Q(#{str}) else %Q(#{str}) end end def inline_eq(id) str = super(id) chapter, id = extract_chapter_id(id) if @book.config['chapterlink'] %Q(#{str}) else %Q(#{str}) end end def inline_asis(str, tag) %Q(<#{tag}>#{escape(str)}) end def inline_abbr(str) inline_asis(str, 'abbr') end def inline_acronym(str) inline_asis(str, 'acronym') end def inline_cite(str) inline_asis(str, 'cite') end def inline_dfn(str) inline_asis(str, 'dfn') end def inline_em(str) inline_asis(str, 'em') end def inline_kbd(str) inline_asis(str, 'kbd') end def inline_samp(str) inline_asis(str, 'samp') end def inline_strong(str) inline_asis(str, 'strong') end def inline_var(str) inline_asis(str, 'var') end def inline_big(str) inline_asis(str, 'big') end def inline_small(str) inline_asis(str, 'small') end def inline_sub(str) inline_asis(str, 'sub') end def inline_sup(str) inline_asis(str, 'sup') end def inline_tt(str) if @book.htmlversion == 5 %Q(#{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([#{id}]) rescue warn "image not bound: #{id}" %Q(
    missing image: #{id}
    ) end end def inline_uchar(str) %Q(&#x#{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