# -*- coding: utf-8 -*-
##
## ReVIEW::LATEXBuilderクラスを拡張する
##
require 'review/latexbuilder'
module ReVIEW
defined?(LATEXBuilder) or raise "internal error: LATEXBuilder not found."
class LATEXBuilder
public :print, :puts
def target_name
"latex"
end
## 章や節や項や目のタイトル
def headline(level, label, caption)
with_context(:headline) do
_, anchor = headline_prefix(level)
headname = _headline_name(level) # 'chapter', 'section', 'subsection', ...
headstar = _headline_star(level) # '*' or nil
blank() unless @output.pos == 0
with_context(:caption) do
print macro("#{headname}#{headstar}", compile_inline(caption))
print "\n" unless level >= 5 # \paragraphと\subpararaphでは改行しない
end
if headstar && _headline_toc?(level) # 番号はつかないけど目次には出す場合
## starter-heading.sty を使っているときは \addcontentsline が必要ない
#puts "\\@ifundefined{Chapter}{\\addcontentsline{toc}{#{headname}}{#{compile_inline(caption)}}}{}"
puts "\\ifx\\Chapter\\undefined{\\addcontentsline{toc}{#{headname}}{#{compile_inline(caption)}}}\\fi"
end
if _headline_chapter?(level)
## \Chapter直後の\addvspaceが効くように、
## \lastskipをいったん保存し、\labelのあとで復元する。
puts "\\keeplastskip{"
puts " \\label{#{chapter_label()}}"
puts " \\par\\nobreak"
puts "}"
elsif level >= 5 # 段(Paragraph)と小段(Subparagraph)では
nil # 何もしない、\labelもつけない
else
## \Section最後と\Subsection最初の\addvspaceが効くように、
## \lastskipをいったん保存し、\labelのあとで復元する。
puts "\\keeplastskip{"
puts " \\label{#{sec_label(anchor)}}"
puts " \\label{#{label}}" if label
puts " \\par\\nobreak"
puts "}"
end
end
rescue
error "unknown level: #{level}"
end
private
def _headline_name(level)
return 'part' if @chapter.is_a?(ReVIEW::Book::Part)
return HEADLINE[level] # 1: 'chapter', 2: 'section', ...
end
def _headline_star(level)
return '*' if level > @book.config['secnolevel']
return '*' if @chapter.number.to_s.empty? && level > 1
return nil
end
def _headline_toc?(level)
return level <= @book.config['toclevel'].to_i
end
def _headline_chapter?(level)
return level == 1
end
public
def nonum_begin(level, _label, caption)
blank() unless @output.pos == 0
with_context(:headline) do
with_context(:caption) do
puts macro(HEADLINE[level] + '*', compile_inline(caption))
puts "\\ifx\\Chapter\\undefined"
puts macro('addcontentsline', 'toc', HEADLINE[level], compile_inline(caption))
puts "\\fi"
end
end
end
def notoc_begin(level, _label, caption)
blank() unless @output.pos == 0
with_context(:headline) do
with_context(:caption) do
puts "\\ifx\\Chapter\\undefined"
puts macro(HEADLINE[level] + '*', compile_inline(caption))
puts "\\else"
puts macro(HEADLINE[level] + '[]', compile_inline(caption))
puts "\\fi"
end
end
end
## テーブル
def table(lines, id=nil, caption=nil, option=nil)
super
end
def table_header(id, caption, options)
if id.present? || caption.present?
@table_caption = true
pos = options[:pos] || 'h'
star = id.present? ? '' : '*'
s = with_context(:caption) { compile_inline(caption || '') }
puts "\\begin{table}[#{pos}]%%#{id}"
puts "\\centering%"
puts macro("reviewtablecaption#{star}", s)
end
begin
puts macro('label', table_label(id)) if id.present?
rescue KeyError
error "no such table: #{id}"
end
end
alias __original_table_begin table_begin
def table_begin(ncols, fontsize: nil)
if fontsize
font = FONTSIZES[fontsize]
puts "\\def\\startertablefont{\\#{font}}" if font
end
__original_table_begin(ncols)
end
## CSVテーブル
def _table_hline()
"\\hline"
end
def _table_bottom(hline: false)
puts "\\hline" if hline
end
def _table_tr(cells, hline: false)
s = "#{cells.join(' & ')} \\\\"
s << " \\hline" if hline
return s
end
## 改行命令「\\」のあとに改行文字「\n」を置かない。
##
## 「\n」が置かれると、たとえば
##
## foo@
{}
## bar
##
## が
##
## foo\\
##
## bar
##
## に展開されてしまう。
## つまり改行のつもりが改段落になってしまう。
def inline_br(_str)
#"\\\\\n" # original
#"\\\\{}" # これだと後続行の先頭に1/4空白が入ってしまう
"\\\\[0pt]" # これなら後続行の先頭に1/4空白が入らない
end
protected
## コードブロック(//program, //terminal, //output)
FONTSIZES = {
"small" => "small",
"x-small" => "footnotesize",
"xx-small" => "scriptsize",
"large" => "large",
"x-large" => "Large",
"xx-large" => "LARGE",
"medium" => "normalsize",
}
def _codeblock_eolmark()
"{\\startereolmark}"
end
def _codeblock_indentmark()
"{\\starterindentchar}"
end
LATEX_ESCAPE_TABLE = {
'{' => '\{',
'}' => '\}',
'%' => '\%',
'$' => '\$',
'#' => '\#',
'&' => '\&',
'_' => '\_',
'\\' => '{\textbackslash}',
'^' => '{\textasciicircum}',
'~' => '{\textasciitilde}',
}
def _render_codeblock(blockname, lines, id, caption_str, opts)
if opts['eolmark']
eolmark = _codeblock_eolmark() # ex: '{\startereolmark}'
else
eolmark = nil
end
#
eol = eolmark ? "#{eolmark}\\par\n" : "\\par\n"
lines = lines.map {|line|
line = _parse_inline(line) {|text|
_convert_and_escape_str(text)
}
if line =~ /\A\n/
"\\mbox{}#{eol}\n"
else
line.gsub(/(?:\\-)?\n/, eol)
end
}
#
indent_width = opts['indent']
if indent_width && indent_width > 0
lines = _add_indent_mark(lines, indent_width)
end
#
default_opts = _codeblock_default_options(blockname)
opts2 = {}
opts.each do |k, v|
opts2[k] = v unless v == default_opts[k]
end
fontsize = FONTSIZES[opts['fontsize']]
#
environ = "starter#{blockname}"
puts "\\begingroup"
puts " \\makeatletter" if fontsize
puts " \\def\\starter@#{blockname}@fontsize{#{fontsize}}" if fontsize
puts " \\makeatother" if fontsize
puts " \\makeatletter" unless opts2.empty?
opts2.each do |k, v|
x = k.gsub(/[^a-zA-Z]/, '_')
v = v == true ? 'Y' : v == false ? '' : v #escape(v.to_s)
puts " \\edef\\starter@#{blockname}@#{x}{#{v}}"
#puts " \\edef\\starter@codeblock@#{x}{#{v}}"
end
puts " \\makeatother" unless opts2.empty?
print "\\begin{#{environ}}[#{id}]{#{caption_str}}"
print "\\startersetfoldmark{}" unless opts['foldmark']
if opts['lineno']
gen = LineNumberGenerator.new(opts['lineno'])
width = opts['linenowidth'] || -1
if width && width >= 0
if width == 0
last_lineno = gen.each.take(lines.length).compact.last
width = last_lineno.to_s.length
end
print "\\startersetfoldindentwidth{#{'9'*(width+2)}}"
format = "\\starterinnerlineno{%#{width}s:} "
else
format = "\\starterouterlineno{%s}"
end
buf = []
lines.zip(gen).each do |x, n|
buf << "#{(format % n.to_s).gsub(' ', '~')}#{x}"
end
print buf.join()
else
print lines.join()
end
puts "\\end{#{environ}}"
puts "\\endgroup"
nil
end
def _convert_and_escape_str(str)
#str.gsub(/[\{\}\\\%\^\_\$\&\~]/, LATEX_ESCAPE_TABLE)
rexp = /[\{\}\\\#\%\^\_\$\&\~]/
cs = []
zenkaku_rexp = ZENKAKU_CHAR_REXP
str.each_char do |c|
cs << (
case c
when / / ; '~'
when /\n/ ; "\n"
when rexp ; LATEX_ESCAPE_TABLE[c]
when zenkaku_rexp ; "\\ZC{#{c}}" # 全角文字
else ; c # 半角文字
end
)
end
cs << "" unless cs[-1] == "\n"
return cs.join('\\-')
end
ZENKAKU_CHAR_REXP = /\A[^\000-\177]\z/
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
## ・\caption{} のかわりに \reviewimagecaption{} を使うよう修正
## ・「scale=X」に加えて「pos=X」も受け付けるように拡張
def _render_image(id, image_filepath, caption, opts)
width = "\\maxwidth"
if opts[:scale]
case (scale = opts[:scale])
when /\A\d+\z/, /\A\d\.\d*\z/, /\A\.\d+\z/
when /\A\d+(\.\d+)?%\z/
scale = scale.sub(/%\z/, '').to_f / 100.0
else
error "scale=#{scale}: invalid scale value."
end
width = "#{scale}\\maxwidth" # not '\textwidth'
end
#
if opts[:width]
case opts[:width]
when /\A\d+\z/, /\A\d\.\d*\z/, /\A\.\d+\z/
width = "#{opts[:width]}\\textwidth" # not '\maxwidth'
when /\A(\d+(\.\d+)?)%\z/
width = "#{$1.to_f/100.0}\\textwidth" # not '\maxwidth'
when /\A(\d+(?:\.\d*)?)(mm|cm)\z/
width = "#{$1}true#{$2}" # 'mm'->'truemm', 'cm'->'truecm'
else
width = opts[:width]
end
end
#
metrics = ["width=#{width}"]
metrics << "draft=#{opts[:draft]}" if opts[:draft] != nil
#
pos = opts[:pos] || config_starter()['image_position']
puts "\\begin{reviewimage}[#{pos}]%%#{id}"
puts "\\starterimageframe{%" if opts[:border]
puts "\\includegraphics[#{metrics.join(',')}]{#{image_filepath}}%"
puts "}%" if opts[:border]
with_context(:caption) do
#puts macro('caption', compile_inline(caption)) if caption.present? # original
puts "\\reviewimagecaption{#{compile_inline(caption)}}" if caption.present?
end
puts "\\label{#{image_label(id)}}"
puts "\\end{reviewimage}"
end
## //imgtable
def imgtable(lines, id, caption=nil, option=nil)
super
end
def _render_imgtable(id, caption, opts)
pos = opts['pos'] || 'h'
puts "\\begin{table}[#{pos}]%%#{id}"
puts "\\centering"
yield
puts "\\end{table}"
blank()
end
def _render_imgtable_caption(caption)
puts macro('reviewimgtablecaption', compile_inline(caption))
end
def _render_imgtable_label(id)
puts macro('label', table_label(id))
rescue ReVIEW::KeyError
error "no such table: #{id}"
end
def imgtable_image(id, _caption, metric)
metrics = parse_metric('latex', metric)
# image is always bound here
#puts "\\begin{reviewimage}%%#{id}" #-
metrics = "width=\\maxwidth" unless metrics.present?
imagefile = @chapter.image(id).path
puts "\\includegraphics[#{metrics}]{#{imagefile}}"
#puts '\end{reviewimage}' #-
end
##
def _build_secref(chap, num, title, parent_title)
s = ""
## 親セクションのタイトルがあれば使う
if parent_title && self.config_starter['secref_parenttitle']
s << "「%s」内の" % parent_title # TODO: I18n化
end
## 対象セクションへのリンクを作成する
if @book.config['chapterlink']
label = "sec:" + num.gsub('.', '-')
level = num.split('.').length
case level
when 2 ; s << "\\startersecref{#{title}}{#{label}}"
when 3 ; s << "\\startersubsecref{#{title}}{#{label}}"
when 4 ; s << "\\startersubsubsecref{#{title}}{#{label}}"
else
raise "#{num}: unexpected section level (expected: 2~4)."
end
else
s << title
end
return s
end
###
public
def ul_begin
blank()
puts '\begin{starteritemize}' # instead of 'itemize'
end
def ul_end
puts '\end{starteritemize}' # instead of 'itemize'
blank()
end
def ol_begin(start_num=nil)
blank()
puts '\begin{starterenumerate}' # instead of 'enumerate'
if start_num.nil?
return true unless @ol_num
puts "\\setcounter{enumi}{#{@ol_num - 1}}"
@ol_num = nil
end
end
def ol_end
puts '\end{starterenumerate}' # instead of 'enumerate'
blank()
end
def ol_item_begin(lines, num)
str = lines.join
num = escape(num).sub(']', '\rbrack{}')
puts "\\item[#{num}] #{str}"
end
def ol_item_end()
end
def dt(str)
puts "\\starterdt{#{str}}%"
end
def dl_dd_begin()
end
def dl_dd_end()
end
## コラム
def column_begin(level, label, caption)
blank()
@doc_status[:column] = true
puts "\\begin{reviewcolumn}\n"
puts "\\phantomsection % for hyperref" #+
if label
puts "\\hypertarget{#{column_label(label)}}{}"
else
puts "\\hypertarget{#{column_label(caption)}}{}"
end
@doc_status[:caption] = true
puts macro('reviewcolumnhead', nil, compile_inline(caption))
@doc_status[:caption] = nil
if level <= @book.config['toclevel'].to_i
#puts "\\addcontentsline{toc}{#{HEADLINE[level]}}{#{compile_inline(caption)}}" #-
puts "\\addcontentsline{toc}{#{HEADLINE[level]}}{\\numberline{#{toc_column()}}#{compile_inline(caption)}}" #+
end
end
def toc_column
#escape('コラム:')
escape('[コラム]')
end
#### ブロック命令
## 導入文(//lead{ ... //})のデザインをLaTeXのスタイルファイルで
## 変更できるよう、マクロを使う。
def lead(lines)
puts '\begin{starterlead}' # オリジナルは \begin{quotation}
puts lines
puts '\end{starterlead}'
end
## 章 (Chapter) の概要
## (導入文 //lead{ ... //} と似ているが、導入文では詩や物語を
## 引用するのが普通らしく、概要 (abstract) とは違うみたいなので、
## 概要を表すブロックを用意した。)
def on_abstract_block()
puts '\begin{starterabstract}'
yield
puts '\end{starterabstract}'
end
## 章 (Chapter) の著者
def on_chapterauthor_block(name)
puts "\\starterchapterauthor{#{escape(name)}}"
end
## 会話リスト
def _render_talklist(opts, &b)
puts '\begingroup'
puts ' \makeatletter' unless opts.empty?
opts.each do |k, v|
puts " \\def\\starter@talklist@#{k}{#{v}}"
end
puts ' \makeatother' unless opts.empty?
puts '\begin{startertalklist}'
yield
puts '\end{startertalklist}'
puts '\endgroup'
end
## 会話項目
def _render_talk(image_filepath=nil, name=nil, &b)
s = name ? compile_inline(name) : nil
print "\\startertalk{#{image_filepath}}{#{s}}{%"
yield
@blank_needed = false
puts "}"
end
## キーと説明文のリスト
def _render_desclist(opts, &b)
bkup = @_desclist_opts
@_desclist_opts = opts
star = opts[:compact] ? '*' : ''
puts "\\begingroup"
puts " \\makeatletter" unless opts.empty?
opts.each do |k, v|
k_ = k.to_s.gsub('_', '@')
v_ = v == true ? 'Y' : v == false ? '' : escape(v.to_s)
puts " \\def\\starter@desclist@#{k_}{#{v_}}"
end
puts " \\makeatother" unless opts.empty?
puts "\\begin{starterdesclist}"
yield
puts "\\end{starterdesclist}"
puts "\\endgroup"
@_desclist_opts = bkup
end
## キーと説明文
def on_desc_block(key, text=nil, &b)
text = "\n" + text if text
super(key, text, &b)
end
def _render_desc(key, &b)
opts = @_desclist_opts
s = compile_inline(key)
#print "\\begin{starterdesc}{#{s}}"
#print "\\begin{starterdesc}{#{s}}%"
print "\\begin{starterdesc}{#{s}}\\ignorespaces "
yield
@blank_needed = false
puts "\\end{starterdesc}"
end
## 縦方向の空きを入れる
def _render_vspace(size)
puts "\\vspace{#{size}}"
end
def _render_addvspace(size)
puts "\\addvspace{#{size}}"
end
## 章タイトルを独立したページに
def makechaptitlepage(option=nil)
case option
when nil, "" ;
when 'toc=section', 'toc=subsection' ;
when 'toc', 'toc=on'
option = "toc=on"
else
raise ArgumentError.new("//makechaptitlepage[#{option}]: unknown option (expected 'toc=section' or 'toc=subsection').")
end
puts "\\makechaptitlepage{#{option}}"
end
## 縦方向のスペースがなければ改ページ
def _render_needvspace(height)
puts "\\needvspace{#{height}}"
end
## 段(Paragraph)の終わりにスペースを入れる
def paragraphend()
puts "\\ParagraphEnd"
end
## 小段(Subparagraph)の終わりにスペースを入れる(あれば)
def subparagraphend()
puts "\\SubparagraphEnd"
end
## 引用(複数段落に対応)
## (入れ子対応なので、中に箇条書きや別のブロックを入れられる)
def on_quote_block()
puts '\begin{starterquote}'
yield
puts '\end{starterquote}'
end
def quote(lines)
on_quote_block() do
puts lines
end
end
## 引用 (====[quote] ... ====[/quote])
## (ブロック構文ではないので、中に箇条書きや別のブロックを入れられる)
def quote_begin(level, label, caption)
puts '\begin{starterquote}'
end
def quote_end(level)
puts '\end{starterquote}'
end
## ノート(//note[caption]{ ... //})
## (入れ子対応なので、中に箇条書きや別のブロックを入れられる)
def on_note_block(label=nil, caption=nil)
with_context(:minicolumn) do
caption, label = label, nil if caption.nil?
s = compile_inline(caption || "")
puts "\\begin{starternote}[#{label}]{#{s}}"
yield
puts "\\end{starternote}"
end
end
def note(lines, label=nil, caption=nil)
on_note_block(label, caption) do
puts lines
end
end
## ノート (====[note] ... ====[/note])
## (ブロック構文ではないので、中に箇条書きや別のブロックを入れられる)
def note_begin(level, label, caption)
enter_context(:note)
s = compile_inline(caption || "")
puts "\\begin{starternote}[#{label}]{#{s}}"
end
def note_end(level)
puts "\\end{starternote}"
exit_context(:note)
end
## コードリスト(//list, //emlist, //listnum, //emlistnum, //cmd, //source)
## TODO: code highlight support
def list(lines, id=nil, caption=nil, lang=nil)
program(lines, id, caption, _codeblock_optstr(lang, false))
end
def listnum(lines, id=nil, caption=nil, lang=nil)
program(lines, id, caption, _codeblock_optstr(lang, true))
end
def emlist(lines, caption=nil, lang=nil)
program(lines, nil, caption, _codeblock_optstr(lang, false))
end
def emlistnum(lines, caption=nil, lang=nil)
program(lines, nil, caption, _codeblock_optstr(lang, true))
end
def source(lines, caption=nil, lang=nil)
program(lines, nil, caption, _codeblock_optstr(lang, false))
end
def cmd(lines, caption=nil, lang=nil)
terminal(lines, nil, caption, _codeblock_optstr(lang, false))
end
def _codeblock_optstr(lang, lineno_flag)
arr = []
arr << lang if lang
if lineno_flag
first_line_num = line_num()
arr << "lineno=#{first_line_num}"
arr << "linenowidth=0"
end
return arr.join(",")
end
private :_codeblock_optstr
## 画像横に文章
def _render_sideimage(filepath, imagewidth, opts, &b)
side = opts['side'] || 'L'
normalize = proc {|s|
rexp = /\A(\d+(?:\.\d+)?)(%|mm|cm)\z/
if s !~ rexp ; s
elsif $2 == '%' ; "#{$1.to_f/100.0}\\textwidth"
else ; "#{$1}true#{$2}"
end
}
imgwidth = normalize.call(imagewidth)
boxwidth = normalize.call(opts['boxwidth']) || imgwidth
sepwidth = normalize.call(opts['sep'] || "0pt")
puts "{\n"
puts " \\def\\starterminiimageframe{Y}\n" if opts['border']
puts " \\begin{startersideimage}{#{side}}{#{filepath}}{#{imgwidth}}{#{boxwidth}}{#{sepwidth}}{}\n"
yield
puts " \\end{startersideimage}\n"
puts "}\n"
end
## 入れ子可能なブロック命令
def on_minicolumn(type, caption=nil, &b)
with_context(:minicolumn) do
s = caption ? with_context(:caption) { compile_inline(caption) } : nil
puts "\\begin{starter#{type}}{#{s}}"
yield
puts "\\end{starter#{type}}\n"
end
end
protected :on_minicolumn
## 数式
def texequation(lines, label=nil, caption=nil)
blank()
#
if label.present?
puts macro("begin", "reviewequationblock")
chap = get_chap()
if chap.nil?
key = "format_number_header_without_chapter"
args = [@chapter.equation(label).number]
else
key = "format_number_header"
args = [chap, @chapter.equation(label).number]
end
s1 = I18n.t("equation")
s2 = I18n.t(key, args)
s3 = I18n.t("caption_prefix")
s4 = compile_inline(caption)
puts macro("reviewequationcaption", "#{s1}#{s2}#{s3}#{s4}")
has_caption_line = true
elsif caption.present?
puts macro("begin", "reviewequationblock")
s3 = I18n.t("caption_prefix")
s4 = compile_inline(caption)
puts macro("reviewequationcaption", "#{s3}#{s4}")
has_caption_line = true
else
has_caption_line = false
end
#
puts macro("begin", "equation*")
lines.each do |line|
puts unescape_latex(line)
end
puts macro("end", "equation*")
#
if has_caption_line
puts macro("end", "reviewequationblock")
end
#
blank()
end
#### インライン命令
## 改段落(箇条書き内では空行を入れられないため)
def inline_par(arg)
"\\starterpar{#{arg}}"
end
## ファイル名
def inline_file(str)
on_inline_file { escape(str) }
end
def on_inline_file
"\\starterfile{#{yield}}"
end
## ユーザ入力
def inline_userinput(str)
on_inline_userinput { escape(str) }
end
def on_inline_userinput
#if within_codeblock?()
# "{\\starteruserinput{\\seqsplit{#{yield}}}}"
#else
"\\starteruserinput{#{yield}}"
#end
end
## 引数をそのまま表示
## 例:
## //emlist{
## @{ABC} ← 太字の「ABC」が表示される
## @$@{ABC}$ ← 「@{ABC}」がそのまま表示される
## //}
def inline_nop(str)
escape(str || "")
end
alias inline_letitgo inline_nop
## 目立たせない(@{} の反対)
def inline_weak(str)
on_inline_weak { escape(str) }
end
def on_inline_weak
#if within_codeblock?()
# "{\\starterweak{\\seqsplit{#{yield}}}}"
#else
"\\starterweak{#{yield}}"
#end
end
## 文字を小さくする
def inline_small(str) ; on_inline_small { escape(str) } ; end
def inline_xsmall(str) ; on_inline_xsmall { escape(str) } ; end
def inline_xxsmall(str) ; on_inline_xxsmall { escape(str) }; end
def on_inline_small() ; "{\\small{}#{yield}}" ; end
def on_inline_xsmall() ; "{\\footnotesize{}#{yield}}"; end
def on_inline_xxsmall() ; "{\\scriptsize{}#{yield}}" ; end
## 文字を大きくする
def inline_large(str) ; on_inline_large { escape(str) } ; end
def inline_xlarge(str) ; on_inline_xlarge { escape(str) } ; end
def inline_xxlarge(str) ; on_inline_xxlarge { escape(str) }; end
def on_inline_large() ; "{\\large{}#{yield}}" ; end
def on_inline_xlarge() ; "{\\Large{}#{yield}}" ; end
def on_inline_xxlarge() ; "{\\LARGE{}#{yield}}" ; end
## 文字を大きくした@{}
def inline_xstrong(str) ; on_inline_xstring { escape(str) } ; end
def inline_xxstrong(str); on_inline_xxstring { escape(str) }; end
def on_inline_xstrong(&b) ; "{\\Large{}#{on_inline_strong(&b)}}" ; end
def on_inline_xxstrong(&b); "{\\LARGE{}#{on_inline_strong(&b)}}" ; end
## コードブロック中で折り返し箇所を手動で指定する
## (\seqsplit による自動折り返し機能が日本語には効かないので、
## 長い行を日本語の箇所で折り返したいときは @{} を使う)
def inline_foldhere(arg)
return '\starterfoldhere{}'
end
## ターミナルでのカーソル(背景が白、文字が黒)
def inline_cursor(str)
"{\\startercursor{#{escape(str)}}}"
end
## 脚注(「//footnote」の脚注テキストを「@{}」でパースすることに注意)
def inline_fn(id)
if @book.config['footnotetext']
macro("footnotemark[#{@chapter.footnote(id).number}]", '')
elsif @doc_status[:caption] || @doc_status[:table] || @doc_status[:column]
@foottext[id] = @chapter.footnote(id).number
macro('protect\\footnotemark', '')
else
with_context(:footnote) { #+
macro('footnote', compile_inline(@chapter.footnote(id).content.strip))
} #+
end
rescue KeyError
error "unknown footnote: #{id}"
end
## nestable inline commands
def on_inline_i() ; "{\\reviewit{#{yield}}}" ; end
#def on_inline_b() ; "{\\reviewbold{#{yield}}}" ; end
#def on_inline_tt() ; "{\\reviewtt{#{yield}}}" ; end
def on_inline_tti() ; "{\\reviewtti{#{yield}}}" ; end
def on_inline_ttb() ; "{\\reviewttb{#{yield}}}" ; end
#def on_inline_code() ; "{\\reviewcode{#{yield}}}" ; end
#def on_inline_del() ; "{\\reviewstrike{#{yield}}}" ; end
def on_inline_sub() ; "{\\textsubscript{#{yield}}}" ; end
def on_inline_sup() ; "{\\textsuperscript{#{yield}}}"; end
def on_inline_em() ; "{\\reviewem{#{yield}}}" ; end
def on_inline_strong(); "{\\reviewstrong{#{yield}}}" ; end
def on_inline_u() ; "{\\reviewunderline{#{yield}}}"; end
def on_inline_ami() ; "{\\reviewami{#{yield}}}" ; end
def on_inline_balloon(); "{\\reviewballoon{#{yield}}}" ; end
def on_inline_tt()
return "{\\reviewtt{#{yield}}}"
end
def on_inline_code()
with_context(:inline_code) {
## 連続した空白が1つの空白として扱われるのを防ぐ
s = yield
s = s.gsub(/ /, '{\\starterspacechar}')
## 連続した「`」や「'」はエスケープする必要がある
s = s.gsub(/\'\'/, "{'}{'}")
s = s.gsub(/\`\`/, '{`}{`}')
## コンテキストによって、背景色をつけないことがある
if false
elsif within_context?(:headline) # 章タイトルや節タイトルでは
"{\\reviewcode[headline]{#{s}}}" # 背景色をつけない(かも)
elsif within_context?(:caption) # リストや画像のキャプションでも
"{\\reviewcode[caption]{#{s}}}" # 背景色をつけない(かも)
else # それ以外では
"{\\reviewcode{#{s}}}" # 背景色をつける(かも)
end
}
end
## @{} が //terminal{ ... //} で効くように上書き
def inline_b(str)
on_inline_b { escape(str) }
end
def on_inline_b() # nestable
if within_codeblock?()
#"{\\bfseries #{yield}}" # \seqsplit{} 内では余計な空白が入る
#"{\\bfseries{}#{yield}}" # \seqsplit{} 内では後続も太字化する
"\\bfseries{}#{yield}\\mdseries{}" # \seqsplit{} 内でうまく効く
else
macro('reviewbold', yield)
end
end
## @{} が //list や //terminal で効くように上書き
def inline_del(str)
on_inline_del { escape(str) }
end
def on_inline_del()
if within_codeblock?()
#"\\reviewstrike{#{yield}}" # \seqsplit{} 内でエラーになる
#"{\\reviewstrike{#{yield}}}" # \seqsplit{} 内でもエラーにならないが折り返しされない
#"{\\reviewstrike{\\seqsplit{#{yield}}}}" # エラーにならないし、折り返しもされる
"{\\reviewstrike{#{yield}}}"
else
macro('reviewstrike', yield)
end
end
def build_inline_href(url, escaped_label) # compile_href()をベースに改造
flag_footnote = self.config_starter['linkurl_footnote']
return _inline_hyperlink(url, escaped_label, flag_footnote)
end
## @{} の代わり
def inline_hlink(str)
url, label = str.split(/, /, 2)
flag_footnote = self.config_starter['hyperlink_footnote']
label_ = label.present? ? escape(label) : nil
return _inline_hyperlink(url, label_, flag_footnote)
end
def _inline_hyperlink(url, escaped_label, flag_footnote)
if /\A[a-z]+:/ !~ url
"\\ref{#{url}}"
elsif ! escaped_label.present?
#"\\url{#{escape_url(url)}}"
"\\starterurl{#{escape_url(url)}}{#{escape(url)}}"
elsif ! flag_footnote
"\\href{#{escape_url(url)}}{#{escaped_label}}"
elsif within_context?(:footnote)
#"#{escaped_label}(\\url{#{escape_url(url)}})"
"#{escaped_label}(\\starterurl{#{escape_url(url)}}{#{escape(url)}})"
else
#"#{escaped_label}\\footnote{\\url{#{escape_url(url)}}}"
#"#{escaped_label}\\footnote{\\starterurl{#{escape_url(url)}}{#{escape(url)}}}"
url1 = escape_url(url)
url2 = escape(url)
"\\href{#{url1}}{#{escaped_label}}\\footnote{\\starterurl{#{url1}}{#{url2}}}"
end
end
private :_inline_hyperlink
def build_inline_ruby(escaped_word, escaped_yomi) # compile_ruby()をベースに改造
"\\ruby{#{escaped_word}}{#{escaped_yomi}}"
end
def inline_bou(str)
## original
#str.split(//).map { |c| macro('ruby', escape(c), macro('textgt', BOUTEN)) }.join('\allowbreak')
## work well with XeLaTeX as well as upLaTeX
str.split(//).map {|c| "\\ruby{#{escape(c)}}{#{BOUTEN}}" }.join('\allowbreak')
end
protected
## ノートを参照する
def build_noteref(chapter, label, caption)
"\\starternoteref{#{label}}{#{escape(caption)}}"
end
## 数式を参照する
def build_eq(chapter, label, number)
#"\\reviewequationref{#{chapter.number}.#{number}}"
escape("#{I18n.t('equation')}#{chapter.number}.#{number}")
end
public
## 数式を $...$ から \(...\) に変更する。
## これで //list の中でも @$...$ が使えるようになる。
## ただし '^' や '_' がエスケープされるので、実用性はイマイチ。
def inline_m(str)
#" $#{str}$ " # original
"\\(#{str}\\)"
end
## 「``」と「''」で囲む
def on_inline_qq()
"``#{yield}''"
end
## 索引に載せる語句 (@{}, @{})
def inline_idx(str)
s1, s2 = _compile_term(str)
"#{s1}\\index{#{s2}}"
end
def inline_hidx(str)
_, s2 = _compile_term(str)
"\\index{#{s2}}"
end
def inline_term(str)
s1, s2 = _compile_term(str)
"\\starterterm{#{s1}}\\index{#{s2}}"
end
def on_inline_termnoidx()
"\\starterterm{#{yield}}"
end
def _compile_term(str)
arr = []
placeholder = "\\starterindexplaceholder{}"
display_str, see = parse_term(str, placeholder) do |term, term_e, yomi|
if yomi
arr << "#{escape_index(escape_latex(yomi))}@#{term_e}"
elsif escape_index(term) != term_e
arr << "#{escape_index(term)}@#{term_e}"
else
arr << term_e
end
end
argstr = arr.join('!')
argstr += "|see{#{escape_latex(see)}}" if see
return display_str, argstr
end
private :_compile_term
end
end