review-retrovert 0.3.3 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/retrovert.yml +39 -0
- data/.gitignore +2 -2
- data/.ruby-version +1 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +67 -1
- data/README.md +6 -3
- data/lib/review/retrovert/converter.rb +231 -6
- data/lib/review/retrovert/version.rb +1 -1
- data/review-retrovert.gemspec +1 -1
- data/testdata/mybook/.gitignore +7 -0
- data/testdata/mybook/README.md +43 -0
- data/testdata/mybook/Rakefile +16 -0
- data/testdata/mybook/catalog.yml +33 -0
- data/testdata/mybook/config-starter.yml +139 -0
- data/testdata/mybook/config.yml +375 -0
- data/testdata/mybook/contents/00-preface.re +83 -0
- data/testdata/mybook/contents/01-install.re +272 -0
- data/testdata/mybook/contents/02-tutorial.re +1008 -0
- data/testdata/mybook/contents/03-syntax.re +2613 -0
- data/testdata/mybook/contents/04-customize.re +728 -0
- data/testdata/mybook/contents/05-faq.re +328 -0
- data/testdata/mybook/contents/06-bestpractice.re +971 -0
- data/testdata/mybook/contents/91-compare.re +18 -0
- data/testdata/mybook/contents/92-filelist.re +119 -0
- data/testdata/mybook/contents/93-background.re +267 -0
- data/testdata/mybook/contents/99-postface.re +38 -0
- data/testdata/mybook/css/normalize.css +349 -0
- data/testdata/mybook/css/webstyle.css +514 -0
- data/testdata/mybook/images/03-syntax/favicon-16x16.png +0 -0
- data/testdata/mybook/images/03-syntax/figure_heretop.png +0 -0
- data/testdata/mybook/images/03-syntax/tw-icon1.jpg +0 -0
- data/testdata/mybook/images/03-syntax/tw-icon2.jpg +0 -0
- data/testdata/mybook/images/03-syntax/tw-icon3.jpg +0 -0
- data/testdata/mybook/images/03-syntax/tw-icon4.jpg +0 -0
- data/testdata/mybook/images/04-customize/caption_pagebreak.png +0 -0
- data/testdata/mybook/images/04-customize/chaptitlepage_sample.png +0 -0
- data/testdata/mybook/images/05-faq/codeblock_rpadding1.png +0 -0
- data/testdata/mybook/images/05-faq/codeblock_rpadding2.png +0 -0
- data/testdata/mybook/images/06-bestpractice/figure_heretop.png +0 -0
- data/testdata/mybook/images/06-bestpractice/font_beramono.png +0 -0
- data/testdata/mybook/images/06-bestpractice/heading_design1.png +0 -0
- data/testdata/mybook/images/06-bestpractice/heading_design2.png +0 -0
- data/testdata/mybook/images/06-bestpractice/margin_book.png +0 -0
- data/testdata/mybook/images/06-bestpractice/multiline-title.png +0 -0
- data/testdata/mybook/images/06-bestpractice/preface_numbered.png +0 -0
- data/testdata/mybook/images/06-bestpractice/program_border.png +0 -0
- data/testdata/mybook/images/06-bestpractice/sechead_design_4.png +0 -0
- data/testdata/mybook/images/06-bestpractice/titlepage-samples.png +0 -0
- data/testdata/mybook/images/93-background/bug913.png +0 -0
- data/testdata/mybook/images/93-background/slide2.png +0 -0
- data/testdata/mybook/images/cover_a5.pdf +0 -0
- data/testdata/mybook/images/cover_b5.pdf +0 -0
- data/testdata/mybook/images/tw-icon.jpg +0 -0
- data/testdata/mybook/layouts/layout.epub.erb +29 -0
- data/testdata/mybook/layouts/layout.html5.erb +106 -0
- data/testdata/mybook/layouts/layout.tex.erb +546 -0
- data/testdata/mybook/lib/hooks/beforetexcompile.rb +55 -0
- data/testdata/mybook/lib/ruby/review-builder.rb +503 -0
- data/testdata/mybook/lib/ruby/review-cli.rb +58 -0
- data/testdata/mybook/lib/ruby/review-compiler.rb +523 -0
- data/testdata/mybook/lib/ruby/review-epubmaker.rb +606 -0
- data/testdata/mybook/lib/ruby/review-htmlbuilder.rb +661 -0
- data/testdata/mybook/lib/ruby/review-latexbuilder.rb +782 -0
- data/testdata/mybook/lib/ruby/review-maker.rb +235 -0
- data/testdata/mybook/lib/ruby/review-monkeypatch.rb +91 -0
- data/testdata/mybook/lib/ruby/review-pdfmaker.rb +468 -0
- data/testdata/mybook/lib/ruby/review-textbuilder.rb +36 -0
- data/testdata/mybook/lib/ruby/review-tocparser.rb +285 -0
- data/testdata/mybook/lib/ruby/review-webmaker.rb +433 -0
- data/testdata/mybook/lib/tasks/mytasks.rake +31 -0
- data/testdata/mybook/lib/tasks/review.rake +142 -0
- data/testdata/mybook/lib/tasks/review.rake.orig +72 -0
- data/testdata/mybook/lib/tasks/starter.rake +326 -0
- data/testdata/mybook/locale.yml +6 -0
- data/testdata/mybook/review-ext.rb +206 -0
- data/testdata/mybook/sty/jumoline.sty +310 -0
- data/testdata/mybook/sty/mycolophon.sty +81 -0
- data/testdata/mybook/sty/mystyle.sty +8 -0
- data/testdata/mybook/sty/mytextsize.sty +61 -0
- data/testdata/mybook/sty/mytitlepage.sty +103 -0
- data/testdata/mybook/sty/reviewmacro.sty +60 -0
- data/testdata/mybook/sty/starter-codeblock.sty +332 -0
- data/testdata/mybook/sty/starter-color.sty +79 -0
- data/testdata/mybook/sty/starter-font.sty +112 -0
- data/testdata/mybook/sty/starter-heading.sty +514 -0
- data/testdata/mybook/sty/starter-note.sty +127 -0
- data/testdata/mybook/sty/starter-section.sty +262 -0
- data/testdata/mybook/sty/starter-toc.sty +72 -0
- data/testdata/mybook/sty/starter.sty +554 -0
- data/testdata/mybook/style.css +597 -0
- metadata +100 -3
@@ -0,0 +1,782 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
##
|
4
|
+
## ReVIEW::LATEXBuilderクラスを拡張する
|
5
|
+
##
|
6
|
+
|
7
|
+
require 'review/latexbuilder'
|
8
|
+
|
9
|
+
|
10
|
+
module ReVIEW
|
11
|
+
|
12
|
+
defined?(LATEXBuilder) or raise "internal error: LATEXBuilder not found."
|
13
|
+
|
14
|
+
|
15
|
+
class LATEXBuilder
|
16
|
+
|
17
|
+
## 章や節や項や目のタイトル
|
18
|
+
def headline(level, label, caption)
|
19
|
+
with_context(:headline) do
|
20
|
+
_, anchor = headline_prefix(level)
|
21
|
+
headname = _headline_name(level) # 'chapter', 'section', 'subsection', ...
|
22
|
+
headstar = _headline_star(level) # '*' or nil
|
23
|
+
blank unless @output.pos == 0
|
24
|
+
with_context(:caption) do
|
25
|
+
print macro("#{headname}#{headstar}", compile_inline(caption))
|
26
|
+
print "\n" unless level >= 5 # \paragraphと\subpararaphでは改行しない
|
27
|
+
end
|
28
|
+
if headstar && _headline_toc?(level) # 番号はつかないけど目次には出す場合
|
29
|
+
## starter-heading.sty を使っているときは \addcontentsline が必要ない
|
30
|
+
#puts "\\@ifundefined{Chapter}{\\addcontentsline{toc}{#{headname}}{#{compile_inline(caption)}}}{}"
|
31
|
+
puts "\\ifx\\Chapter\\undefined{\\addcontentsline{toc}{#{headname}}{#{compile_inline(caption)}}}\\fi"
|
32
|
+
end
|
33
|
+
if _headline_chapter?(level)
|
34
|
+
puts macro('label', chapter_label)
|
35
|
+
elsif level >= 5 # 段(Paragraph)と小段(Subparagraph)では
|
36
|
+
nil # 何もしない、\labelもつけない
|
37
|
+
else
|
38
|
+
## \Section最後と\Subsection最初の\addvspaceが効くように、
|
39
|
+
## \lastskipをいったん保存し、\labelのあとで復元する。
|
40
|
+
puts "\\keeplastskip{"
|
41
|
+
puts " \\label{#{sec_label(anchor)}}"
|
42
|
+
puts " \\label{#{label}}" if label
|
43
|
+
puts " \\par\\nobreak"
|
44
|
+
puts "}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
rescue
|
48
|
+
error "unknown level: #{level}"
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def _headline_name(level)
|
54
|
+
return 'part' if @chapter.is_a?(ReVIEW::Book::Part)
|
55
|
+
return HEADLINE[level] # 1: 'chapter', 2: 'section', ...
|
56
|
+
end
|
57
|
+
|
58
|
+
def _headline_star(level)
|
59
|
+
return '*' if level > @book.config['secnolevel']
|
60
|
+
return '*' if @chapter.number.to_s.empty? && level > 1
|
61
|
+
return nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def _headline_toc?(level)
|
65
|
+
return level <= @book.config['toclevel'].to_i
|
66
|
+
end
|
67
|
+
|
68
|
+
def _headline_chapter?(level)
|
69
|
+
return level == 1
|
70
|
+
end
|
71
|
+
|
72
|
+
public
|
73
|
+
|
74
|
+
def nonum_begin(level, _label, caption)
|
75
|
+
blank unless @output.pos == 0
|
76
|
+
with_context(:headline) do
|
77
|
+
with_context(:caption) do
|
78
|
+
puts macro(HEADLINE[level] + '*', compile_inline(caption))
|
79
|
+
puts "\\ifx\\Chapter\\undefined"
|
80
|
+
puts macro('addcontentsline', 'toc', HEADLINE[level], compile_inline(caption))
|
81
|
+
puts "\\fi"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def notoc_begin(level, _label, caption)
|
87
|
+
blank unless @output.pos == 0
|
88
|
+
with_context(:headline) do
|
89
|
+
with_context(:caption) do
|
90
|
+
puts "\\ifx\\Chapter\\undefined"
|
91
|
+
puts macro(HEADLINE[level] + '*', compile_inline(caption))
|
92
|
+
puts "\\else"
|
93
|
+
puts macro(HEADLINE[level] + '[]', compile_inline(caption))
|
94
|
+
puts "\\fi"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
## テーブルヘッダー
|
100
|
+
## (TODO: 第3引数にpos=htbpを指定)
|
101
|
+
def table_header(id, caption)
|
102
|
+
if caption.present?
|
103
|
+
@table_caption = true
|
104
|
+
star = id.present? ? '' : '*'
|
105
|
+
s = with_context(:caption) { compile_inline(caption) }
|
106
|
+
puts "\\begin{table}[h]%%#{id}"
|
107
|
+
puts "\\centering%"
|
108
|
+
puts macro("reviewtablecaption#{star}", s)
|
109
|
+
end
|
110
|
+
puts macro('label', table_label(id)) if id.present?
|
111
|
+
end
|
112
|
+
|
113
|
+
## 改行命令「\\」のあとに改行文字「\n」を置かない。
|
114
|
+
##
|
115
|
+
## 「\n」が置かれると、たとえば
|
116
|
+
##
|
117
|
+
## foo@<br>{}
|
118
|
+
## bar
|
119
|
+
##
|
120
|
+
## が
|
121
|
+
##
|
122
|
+
## foo\\
|
123
|
+
##
|
124
|
+
## bar
|
125
|
+
##
|
126
|
+
## に展開されてしまう。
|
127
|
+
## つまり改行のつもりが改段落になってしまう。
|
128
|
+
def inline_br(_str)
|
129
|
+
#"\\\\\n" # original
|
130
|
+
#"\\\\{}" # これだと後続行の先頭に1/4空白が入ってしまう
|
131
|
+
"\\\\[0pt]" # これなら後続行の先頭に1/4空白が入らない
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
## コードブロック(//program, //terminal)
|
136
|
+
|
137
|
+
def program(lines, id=nil, caption=nil, optionstr=nil)
|
138
|
+
_codeblock('program', lines, id, caption, optionstr)
|
139
|
+
end
|
140
|
+
|
141
|
+
def terminal(lines, id=nil, caption=nil, optionstr=nil)
|
142
|
+
_codeblock('terminal', lines, id, caption, optionstr)
|
143
|
+
end
|
144
|
+
|
145
|
+
protected
|
146
|
+
|
147
|
+
FONTSIZES = {
|
148
|
+
"small" => "small",
|
149
|
+
"x-small" => "footnotesize",
|
150
|
+
"xx-small" => "scriptsize",
|
151
|
+
"large" => "large",
|
152
|
+
"x-large" => "Large",
|
153
|
+
"xx-large" => "LARGE",
|
154
|
+
}
|
155
|
+
|
156
|
+
## コードブロック(//program, //terminal)
|
157
|
+
def _codeblock(blockname, lines, id, caption, optionstr)
|
158
|
+
## ブロックコマンドのオプション引数はCompilerクラスでパースすべき。
|
159
|
+
## しかしCompilerクラスがそのような設計になってないので、
|
160
|
+
## 仕方ないのでBuilderクラスでパースする。
|
161
|
+
opts = _parse_codeblock_optionstr(optionstr, blockname)
|
162
|
+
CODEBLOCK_OPTIONS.each {|k, v| opts[k] = v unless opts.key?(k) }
|
163
|
+
#
|
164
|
+
if opts['eolmark']
|
165
|
+
lines = lines.map {|line| "#{detab(line)}\\startereolmark{}" }
|
166
|
+
else
|
167
|
+
lines = lines.map {|line| detab(line) }
|
168
|
+
end
|
169
|
+
#
|
170
|
+
if opts['indentwidth'] && opts['indentwidth'] > 0
|
171
|
+
indent_w = opts['indentwidth']
|
172
|
+
indent_str = " " * (indent_w - 1) + "{\\starterindentchar}"
|
173
|
+
lines = lines.map {|line|
|
174
|
+
line.sub(/\A( +)/) {
|
175
|
+
m, n = ($1.length - 1).divmod indent_w
|
176
|
+
" " << indent_str * m << " " * n
|
177
|
+
}
|
178
|
+
}
|
179
|
+
end
|
180
|
+
#
|
181
|
+
if id.present? || caption.present?
|
182
|
+
caption_str = _build_caption_str(id, caption)
|
183
|
+
else
|
184
|
+
caption_str = nil
|
185
|
+
end
|
186
|
+
#
|
187
|
+
fontsize = FONTSIZES[opts['fontsize']]
|
188
|
+
print "\\def\\startercodeblockfontsize{#{fontsize}}\n"
|
189
|
+
#
|
190
|
+
environ = "starter#{blockname}"
|
191
|
+
print "\\begin{#{environ}}[#{id}]{#{caption_str}}"
|
192
|
+
print "\\startersetfoldmark{}" unless opts['foldmark']
|
193
|
+
if opts['eolmark']
|
194
|
+
print "\\startereolmarkdark{}" if blockname == 'terminal'
|
195
|
+
print "\\startereolmarklight{}" if blockname != 'terminal'
|
196
|
+
end
|
197
|
+
if opts['lineno']
|
198
|
+
gen = LineNumberGenerator.new(opts['lineno'])
|
199
|
+
width = opts['linenowidth']
|
200
|
+
if width && width >= 0
|
201
|
+
if width == 0
|
202
|
+
last_lineno = gen.each.take(lines.length).compact.last
|
203
|
+
width = last_lineno.to_s.length
|
204
|
+
end
|
205
|
+
print "\\startersetfoldindentwidth{#{'9'*(width+2)}}"
|
206
|
+
format = "\\textcolor{gray}{%#{width}s:} "
|
207
|
+
else
|
208
|
+
format = "\\starterlineno{%s}"
|
209
|
+
end
|
210
|
+
buf = []
|
211
|
+
opt_fold = opts['fold']
|
212
|
+
lines.zip(gen).each do |x, n|
|
213
|
+
buf << ( opt_fold \
|
214
|
+
? "#{format % n.to_s}\\seqsplit{#{x}}" \
|
215
|
+
: "#{format % n.to_s}#{x}" )
|
216
|
+
end
|
217
|
+
print buf.join("\n")
|
218
|
+
else
|
219
|
+
print "\\seqsplit{" if opts['fold']
|
220
|
+
print lines.join("\n")
|
221
|
+
print "}" if opts['fold']
|
222
|
+
end
|
223
|
+
puts "\\end{#{environ}}"
|
224
|
+
nil
|
225
|
+
end
|
226
|
+
|
227
|
+
public
|
228
|
+
|
229
|
+
## ・\caption{} のかわりに \reviewimagecaption{} を使うよう修正
|
230
|
+
## ・「scale=X」に加えて「pos=X」も受け付けるように拡張
|
231
|
+
def image_image(id, caption, option_str)
|
232
|
+
pos = nil; border = nil; arr = []
|
233
|
+
_each_block_option(option_str) do |k, v|
|
234
|
+
case k
|
235
|
+
when 'pos'
|
236
|
+
v =~ /\A[Hhtb]+\z/ or # H: Here, h: here, t: top, b: bottom
|
237
|
+
raise "//image[][][pos=#{v}]: expected 'pos=H' or 'pos=h'."
|
238
|
+
pos = v # detect 'pos=H' or 'pos=h'
|
239
|
+
when 'border', 'draft'
|
240
|
+
case v
|
241
|
+
when nil ; flag = true
|
242
|
+
when 'on' ; flag = true
|
243
|
+
when 'off'; flag = false
|
244
|
+
else
|
245
|
+
raise "//image[][][#{k}=#{v}]: expected '#{k}=on' or '#{k}=off'"
|
246
|
+
end
|
247
|
+
border = flag if k == 'border'
|
248
|
+
arr << "draft=#{flag}" if k == 'draft'
|
249
|
+
else
|
250
|
+
arr << (v.nil? ? k : "#{k}=#{v}")
|
251
|
+
end
|
252
|
+
end
|
253
|
+
#
|
254
|
+
metrics = parse_metric('latex', arr.join(","))
|
255
|
+
puts "\\begin{reviewimage}[#{pos}]%%#{id}" if pos
|
256
|
+
puts "\\begin{reviewimage}%%#{id}" unless pos
|
257
|
+
metrics = "width=\\maxwidth" unless metrics.present?
|
258
|
+
puts "\\starterimageframe{%" if border
|
259
|
+
puts "\\includegraphics[#{metrics}]{#{@chapter.image(id).path}}%"
|
260
|
+
puts "}%" if border
|
261
|
+
with_context(:caption) do
|
262
|
+
#puts macro('caption', compile_inline(caption)) if caption.present? # original
|
263
|
+
puts macro('reviewimagecaption', compile_inline(caption)) if caption.present?
|
264
|
+
end
|
265
|
+
puts macro('label', image_label(id))
|
266
|
+
puts "\\end{reviewimage}"
|
267
|
+
end
|
268
|
+
|
269
|
+
def _build_secref(chap, num, title, parent_title)
|
270
|
+
s = ""
|
271
|
+
## 親セクションのタイトルがあれば使う
|
272
|
+
if parent_title && @book.config['starter']['secref_parenttitle']
|
273
|
+
s << "「%s」内の" % parent_title # TODO: I18n化
|
274
|
+
end
|
275
|
+
## 対象セクションへのリンクを作成する
|
276
|
+
if @book.config['chapterlink']
|
277
|
+
label = "sec:" + num.gsub('.', '-')
|
278
|
+
level = num.split('.').length
|
279
|
+
case level
|
280
|
+
when 2 ; s << "\\startersecref{#{title}}{#{label}}"
|
281
|
+
when 3 ; s << "\\startersubsecref{#{title}}{#{label}}"
|
282
|
+
when 4 ; s << "\\startersubsubsecref{#{title}}{#{label}}"
|
283
|
+
else
|
284
|
+
raise "#{num}: unexpected section level (expected: 2~4)."
|
285
|
+
end
|
286
|
+
else
|
287
|
+
s << title
|
288
|
+
end
|
289
|
+
return s
|
290
|
+
end
|
291
|
+
|
292
|
+
###
|
293
|
+
|
294
|
+
public
|
295
|
+
|
296
|
+
def ul_begin
|
297
|
+
blank
|
298
|
+
puts '\begin{starteritemize}' # instead of 'itemize'
|
299
|
+
end
|
300
|
+
|
301
|
+
def ul_end
|
302
|
+
puts '\end{starteritemize}' # instead of 'itemize'
|
303
|
+
blank
|
304
|
+
end
|
305
|
+
|
306
|
+
def ol_begin(start_num=nil)
|
307
|
+
blank
|
308
|
+
puts '\begin{starterenumerate}' # instead of 'enumerate'
|
309
|
+
if start_num.nil?
|
310
|
+
return true unless @ol_num
|
311
|
+
puts "\\setcounter{enumi}{#{@ol_num - 1}}"
|
312
|
+
@ol_num = nil
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
def ol_end
|
317
|
+
puts '\end{starterenumerate}' # instead of 'enumerate'
|
318
|
+
blank
|
319
|
+
end
|
320
|
+
|
321
|
+
def ol_item_begin(lines, num)
|
322
|
+
str = lines.join
|
323
|
+
num = escape(num).sub(']', '\rbrack{}')
|
324
|
+
puts "\\item[#{num}] #{str}"
|
325
|
+
end
|
326
|
+
|
327
|
+
def ol_item_end()
|
328
|
+
end
|
329
|
+
|
330
|
+
## コラム
|
331
|
+
|
332
|
+
def column_begin(level, label, caption)
|
333
|
+
blank
|
334
|
+
@doc_status[:column] = true
|
335
|
+
puts "\\begin{reviewcolumn}\n"
|
336
|
+
puts "\\phantomsection % for hyperref" #+
|
337
|
+
if label
|
338
|
+
puts "\\hypertarget{#{column_label(label)}}{}"
|
339
|
+
else
|
340
|
+
puts "\\hypertarget{#{column_label(caption)}}{}"
|
341
|
+
end
|
342
|
+
@doc_status[:caption] = true
|
343
|
+
puts macro('reviewcolumnhead', nil, compile_inline(caption))
|
344
|
+
@doc_status[:caption] = nil
|
345
|
+
if level <= @book.config['toclevel'].to_i
|
346
|
+
#puts "\\addcontentsline{toc}{#{HEADLINE[level]}}{#{compile_inline(caption)}}" #-
|
347
|
+
puts "\\addcontentsline{toc}{#{HEADLINE[level]}}{\\numberline{#{toc_column()}}#{compile_inline(caption)}}" #+
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
def toc_column
|
352
|
+
#escape('コラム:')
|
353
|
+
escape('[コラム]')
|
354
|
+
end
|
355
|
+
|
356
|
+
#### ブロック命令
|
357
|
+
|
358
|
+
## 導入文(//lead{ ... //})のデザインをLaTeXのスタイルファイルで
|
359
|
+
## 変更できるよう、マクロを使う。
|
360
|
+
def lead(lines)
|
361
|
+
puts '\begin{starterlead}' # オリジナルは \begin{quotation}
|
362
|
+
puts lines
|
363
|
+
puts '\end{starterlead}'
|
364
|
+
end
|
365
|
+
|
366
|
+
## 章 (Chapter) の概要
|
367
|
+
## (導入文 //lead{ ... //} と似ているが、導入文では詩や物語を
|
368
|
+
## 引用するのが普通らしく、概要 (abstract) とは違うみたいなので、
|
369
|
+
## 概要を表すブロックを用意した。)
|
370
|
+
def abstract(lines)
|
371
|
+
puts '\begin{starterabstract}'
|
372
|
+
puts lines
|
373
|
+
puts '\end{starterabstract}'
|
374
|
+
end
|
375
|
+
|
376
|
+
## 章タイトルを独立したページに
|
377
|
+
def makechaptitlepage(option=nil)
|
378
|
+
case option
|
379
|
+
when nil, "" ;
|
380
|
+
when 'toc=section', 'toc=subsection' ;
|
381
|
+
when 'toc', 'toc=on'
|
382
|
+
option = "toc=on"
|
383
|
+
else
|
384
|
+
raise ArgumentError.new("//makechaptitlepage[#{option}]: unknown option (expected 'toc=section' or 'toc=subsection').")
|
385
|
+
end
|
386
|
+
puts "\\makechaptitlepage{#{option}}"
|
387
|
+
end
|
388
|
+
|
389
|
+
## 縦方向のスペースがなければ改ページ
|
390
|
+
def needvspace(builder_name, height)
|
391
|
+
if builder_name == 'latex'
|
392
|
+
puts "\\needvspace{#{height}}"
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
## 段(Paragraph)の終わりにスペースを入れる
|
397
|
+
def paragraphend()
|
398
|
+
puts "\\ParagraphEnd"
|
399
|
+
end
|
400
|
+
|
401
|
+
## 小段(Subparagraph)の終わりにスペースを入れる(あれば)
|
402
|
+
def subparagraphend()
|
403
|
+
puts "\\SubparagraphEnd"
|
404
|
+
end
|
405
|
+
|
406
|
+
## 引用(複数段落に対応)
|
407
|
+
## (入れ子対応なので、中に箇条書きや別のブロックを入れられる)
|
408
|
+
def on_quote_block()
|
409
|
+
puts '\begin{starterquote}'
|
410
|
+
yield
|
411
|
+
puts '\end{starterquote}'
|
412
|
+
end
|
413
|
+
def quote(lines)
|
414
|
+
on_quote_block() do
|
415
|
+
puts lines
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
## 引用 (====[quote] ... ====[/quote])
|
420
|
+
## (ブロック構文ではないので、中に箇条書きや別のブロックを入れられる)
|
421
|
+
def quote_begin(level, label, caption)
|
422
|
+
puts '\begin{starterquote}'
|
423
|
+
end
|
424
|
+
def quote_end(level)
|
425
|
+
puts '\end{starterquote}'
|
426
|
+
end
|
427
|
+
|
428
|
+
## ノート(//note[caption]{ ... //})
|
429
|
+
## (入れ子対応なので、中に箇条書きや別のブロックを入れられる)
|
430
|
+
def on_note_block(label=nil, caption=nil)
|
431
|
+
caption, label = label, nil if caption.nil?
|
432
|
+
s = compile_inline(caption || "")
|
433
|
+
puts "\\begin{starternote}[#{label}]{#{s}}"
|
434
|
+
yield
|
435
|
+
puts "\\end{starternote}"
|
436
|
+
end
|
437
|
+
def note(lines, label=nil, caption=nil)
|
438
|
+
on_note_block(label, caption) do
|
439
|
+
puts lines
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
## ノート (====[note] ... ====[/note])
|
444
|
+
## (ブロック構文ではないので、中に箇条書きや別のブロックを入れられる)
|
445
|
+
def note_begin(level, label, caption)
|
446
|
+
enter_context(:note)
|
447
|
+
s = compile_inline(caption || "")
|
448
|
+
puts "\\begin{starternote}[#{label}]{#{s}}"
|
449
|
+
end
|
450
|
+
def note_end(level)
|
451
|
+
puts "\\end{starternote}"
|
452
|
+
exit_context(:note)
|
453
|
+
end
|
454
|
+
|
455
|
+
## コードリスト(//list, //emlist, //listnum, //emlistnum, //cmd, //source)
|
456
|
+
## TODO: code highlight support
|
457
|
+
def list(lines, id=nil, caption=nil, lang=nil)
|
458
|
+
program(lines, id, caption, _codeblock_optstr(lang, false))
|
459
|
+
end
|
460
|
+
def listnum(lines, id=nil, caption=nil, lang=nil)
|
461
|
+
program(lines, id, caption, _codeblock_optstr(lang, true))
|
462
|
+
end
|
463
|
+
def emlist(lines, caption=nil, lang=nil)
|
464
|
+
program(lines, nil, caption, _codeblock_optstr(lang, false))
|
465
|
+
end
|
466
|
+
def emlistnum(lines, caption=nil, lang=nil)
|
467
|
+
program(lines, nil, caption, _codeblock_optstr(lang, true))
|
468
|
+
end
|
469
|
+
def source(lines, caption=nil, lang=nil)
|
470
|
+
program(lines, nil, caption, _codeblock_optstr(lang, false))
|
471
|
+
end
|
472
|
+
def cmd(lines, caption=nil, lang=nil)
|
473
|
+
terminal(lines, nil, caption, _codeblock_optstr(lang, false))
|
474
|
+
end
|
475
|
+
def _codeblock_optstr(lang, lineno_flag)
|
476
|
+
arr = []
|
477
|
+
arr << lang if lang
|
478
|
+
if lineno_flag
|
479
|
+
first_line_num = line_num
|
480
|
+
arr << "lineno=#{first_line_num}"
|
481
|
+
arr << "linenowidth=0"
|
482
|
+
end
|
483
|
+
return arr.join(",")
|
484
|
+
end
|
485
|
+
private :_codeblock_optstr
|
486
|
+
|
487
|
+
|
488
|
+
## 入れ子可能なブロック命令
|
489
|
+
|
490
|
+
def on_minicolumn(type, caption, &b)
|
491
|
+
s = with_context(:caption) { compile_inline(caption) }
|
492
|
+
puts "\\begin{starter#{type}}{#{s}}"
|
493
|
+
yield
|
494
|
+
puts "\\end{starter#{type}}\n"
|
495
|
+
end
|
496
|
+
protected :on_minicolumn
|
497
|
+
|
498
|
+
def on_sideimage_block(imagefile, imagewidth, option_str=nil, &b)
|
499
|
+
imagefile, imagewidth, opts = validate_sideimage_args(imagefile, imagewidth, option_str)
|
500
|
+
filepath = find_image_filepath(imagefile)
|
501
|
+
side = opts['side'] || 'L'
|
502
|
+
normalize = proc {|s|
|
503
|
+
s =~ /\A(\d+(?:\.\d+)?)(%|mm|cm)\z/
|
504
|
+
if $2.nil? ; s
|
505
|
+
elsif $2 == '%' ; "#{$1.to_f/100.0}\\textwidth"
|
506
|
+
else ; "#{$1}true#{$2}"
|
507
|
+
end
|
508
|
+
}
|
509
|
+
imgwidth = normalize.call(imagewidth)
|
510
|
+
boxwidth = normalize.call(opts['boxwidth']) || imgwidth
|
511
|
+
sepwidth = normalize.call(opts['sep'] || "0pt")
|
512
|
+
puts "{\n"
|
513
|
+
puts " \\def\\starterminiimageframe{Y}\n" if opts['border']
|
514
|
+
puts " \\begin{startersideimage}{#{side}}{#{filepath}}{#{imgwidth}}{#{boxwidth}}{#{sepwidth}}{}\n"
|
515
|
+
yield
|
516
|
+
puts " \\end{startersideimage}\n"
|
517
|
+
puts "}\n"
|
518
|
+
end
|
519
|
+
|
520
|
+
def texequation(lines, label=nil, caption=nil)
|
521
|
+
blank()
|
522
|
+
#
|
523
|
+
if label.present?
|
524
|
+
puts macro("begin", "reviewequationblock")
|
525
|
+
chap = get_chap()
|
526
|
+
if chap.nil?
|
527
|
+
key = "format_number_header_without_chapter"
|
528
|
+
args = [@chapter.equation(label).number]
|
529
|
+
else
|
530
|
+
key = "format_number_header"
|
531
|
+
args = [chap, @chapter.equation(label).number]
|
532
|
+
end
|
533
|
+
s1 = I18n.t("equation")
|
534
|
+
s2 = I18n.t(key, args)
|
535
|
+
s3 = I18n.t("caption_prefix")
|
536
|
+
s4 = compile_inline(caption)
|
537
|
+
puts macro("reviewequationcaption", "#{s1}#{s2}#{s3}#{s4}")
|
538
|
+
has_caption_line = true
|
539
|
+
elsif caption.present?
|
540
|
+
puts macro("begin", "reviewequationblock")
|
541
|
+
s3 = I18n.t("caption_prefix")
|
542
|
+
s4 = compile_inline(caption)
|
543
|
+
puts macro("reviewequationcaption", "#{s3}#{s4}")
|
544
|
+
has_caption_line = true
|
545
|
+
else
|
546
|
+
has_caption_line = false
|
547
|
+
end
|
548
|
+
#
|
549
|
+
puts macro("begin", "equation*")
|
550
|
+
lines.each do |line|
|
551
|
+
puts unescape_latex(line)
|
552
|
+
end
|
553
|
+
puts macro("end", "equation*")
|
554
|
+
#
|
555
|
+
if has_caption_line
|
556
|
+
puts macro("end", "reviewequationblock")
|
557
|
+
end
|
558
|
+
#
|
559
|
+
blank()
|
560
|
+
end
|
561
|
+
|
562
|
+
#### インライン命令
|
563
|
+
|
564
|
+
## ファイル名
|
565
|
+
def inline_file(str)
|
566
|
+
on_inline_file { escape(str) }
|
567
|
+
end
|
568
|
+
def on_inline_file
|
569
|
+
"\\starterfile{#{yield}}"
|
570
|
+
end
|
571
|
+
|
572
|
+
## ユーザ入力
|
573
|
+
def inline_userinput(str)
|
574
|
+
on_inline_userinput { escape(str) }
|
575
|
+
end
|
576
|
+
def on_inline_userinput
|
577
|
+
if within_codeblock?()
|
578
|
+
"{\\starteruserinput{\\seqsplit{#{yield}}}}"
|
579
|
+
else
|
580
|
+
"\\starteruserinput{#{yield}}"
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
584
|
+
## 引数をそのまま表示
|
585
|
+
## 例:
|
586
|
+
## //emlist{
|
587
|
+
## @<b>{ABC} ← 太字の「ABC」が表示される
|
588
|
+
## @<nop>$@<b>{ABC}$ ← 「@<b>{ABC}」がそのまま表示される
|
589
|
+
## //}
|
590
|
+
def inline_nop(str)
|
591
|
+
escape(str || "")
|
592
|
+
end
|
593
|
+
alias inline_letitgo inline_nop
|
594
|
+
|
595
|
+
## 目立たせない(@<strong>{} の反対)
|
596
|
+
def inline_weak(str)
|
597
|
+
on_inline_weak { escape(str) }
|
598
|
+
end
|
599
|
+
def on_inline_weak
|
600
|
+
if within_codeblock?()
|
601
|
+
"{\\starterweak{\\seqsplit{#{yield}}}}"
|
602
|
+
else
|
603
|
+
"\\starterweak{#{yield}}"
|
604
|
+
end
|
605
|
+
end
|
606
|
+
|
607
|
+
## 文字を小さくする
|
608
|
+
def inline_small(str) ; on_inline_small { escape(str) } ; end
|
609
|
+
def inline_xsmall(str) ; on_inline_xsmall { escape(str) } ; end
|
610
|
+
def inline_xxsmall(str) ; on_inline_xxsmall { escape(str) }; end
|
611
|
+
def on_inline_small() ; "{\\small{}#{yield}}" ; end
|
612
|
+
def on_inline_xsmall() ; "{\\footnotesize{}#{yield}}"; end
|
613
|
+
def on_inline_xxsmall() ; "{\\scriptsize{}#{yield}}" ; end
|
614
|
+
|
615
|
+
## 文字を大きくする
|
616
|
+
def inline_large(str) ; on_inline_large { escape(str) } ; end
|
617
|
+
def inline_xlarge(str) ; on_inline_xlarge { escape(str) } ; end
|
618
|
+
def inline_xxlarge(str) ; on_inline_xxlarge { escape(str) }; end
|
619
|
+
def on_inline_large() ; "{\\large{}#{yield}}" ; end
|
620
|
+
def on_inline_xlarge() ; "{\\Large{}#{yield}}" ; end
|
621
|
+
def on_inline_xxlarge() ; "{\\LARGE{}#{yield}}" ; end
|
622
|
+
|
623
|
+
## 文字を大きくした@<strong>{}
|
624
|
+
def inline_xstrong(str) ; on_inline_xstring { escape(str) } ; end
|
625
|
+
def inline_xxstrong(str); on_inline_xxstring { escape(str) }; end
|
626
|
+
def on_inline_xstrong(&b) ; "{\\Large{}#{on_inline_strong(&b)}}" ; end
|
627
|
+
def on_inline_xxstrong(&b); "{\\LARGE{}#{on_inline_strong(&b)}}" ; end
|
628
|
+
|
629
|
+
## コードブロック中で折り返し箇所を手動で指定する
|
630
|
+
## (\seqsplit による自動折り返し機能が日本語には効かないので、
|
631
|
+
## 長い行を日本語の箇所で折り返したいときは @<foldhere>{} を使う)
|
632
|
+
def inline_foldhere(arg)
|
633
|
+
return '\starterfoldhere{}'
|
634
|
+
end
|
635
|
+
|
636
|
+
## ターミナルでのカーソル(背景が白、文字が黒)
|
637
|
+
def inline_cursor(str)
|
638
|
+
"{\\startercursor{#{escape(str)}}}"
|
639
|
+
end
|
640
|
+
|
641
|
+
## 脚注(「//footnote」の脚注テキストを「@<fn>{}」でパースすることに注意)
|
642
|
+
def inline_fn(id)
|
643
|
+
if @book.config['footnotetext']
|
644
|
+
macro("footnotemark[#{@chapter.footnote(id).number}]", '')
|
645
|
+
elsif @doc_status[:caption] || @doc_status[:table] || @doc_status[:column]
|
646
|
+
@foottext[id] = @chapter.footnote(id).number
|
647
|
+
macro('protect\\footnotemark', '')
|
648
|
+
else
|
649
|
+
with_context(:footnote) { #+
|
650
|
+
macro('footnote', compile_inline(@chapter.footnote(id).content.strip))
|
651
|
+
} #+
|
652
|
+
end
|
653
|
+
rescue KeyError
|
654
|
+
error "unknown footnote: #{id}"
|
655
|
+
end
|
656
|
+
|
657
|
+
## nestable inline commands
|
658
|
+
|
659
|
+
def on_inline_i() ; "{\\reviewit{#{yield}}}" ; end
|
660
|
+
#def on_inline_b() ; "{\\reviewbold{#{yield}}}" ; end
|
661
|
+
#def on_inline_tt() ; "{\\reviewtt{#{yield}}}" ; end
|
662
|
+
def on_inline_tti() ; "{\\reviewtti{#{yield}}}" ; end
|
663
|
+
def on_inline_ttb() ; "{\\reviewttb{#{yield}}}" ; end
|
664
|
+
#def on_inline_code() ; "{\\reviewcode{#{yield}}}" ; end
|
665
|
+
#def on_inline_del() ; "{\\reviewstrike{#{yield}}}" ; end
|
666
|
+
def on_inline_sub() ; "{\\textsubscript{#{yield}}}" ; end
|
667
|
+
def on_inline_sup() ; "{\\textsuperscript{#{yield}}}"; end
|
668
|
+
def on_inline_em() ; "{\\reviewem{#{yield}}}" ; end
|
669
|
+
def on_inline_strong(); "{\\reviewstrong{#{yield}}}" ; end
|
670
|
+
def on_inline_u() ; "{\\reviewunderline{#{yield}}}"; end
|
671
|
+
def on_inline_ami() ; "{\\reviewami{#{yield}}}" ; end
|
672
|
+
def on_inline_balloon(); "{\\reviewballoon{#{yield}}}" ; end
|
673
|
+
|
674
|
+
def on_inline_tt()
|
675
|
+
## LaTeXでは、'\texttt{}' 中の '!?:.' の直後の空白が2文字分で表示される。
|
676
|
+
## その問題を回避するために、' ' を '\ ' にする。
|
677
|
+
s = yield
|
678
|
+
s = s.gsub(/([!?:.]) /, '\\1\\ ')
|
679
|
+
return "{\\reviewtt{#{s}}}"
|
680
|
+
end
|
681
|
+
|
682
|
+
def on_inline_code()
|
683
|
+
with_context(:inline_code) {
|
684
|
+
## LaTeXでは、'\texttt{}' 中の '!?:.' の直後の空白が2文字分で表示される。
|
685
|
+
## その問題を回避するために、' ' を '\ ' にする。
|
686
|
+
s = yield
|
687
|
+
s = s.gsub(/([!?:.]) /, '\\1\\ ')
|
688
|
+
## コンテキストによって、背景色をつけないことがある
|
689
|
+
if false
|
690
|
+
elsif within_context?(:headline) # 章タイトルや節タイトルでは
|
691
|
+
"{\\reviewcode[headline]{#{s}}}" # 背景色をつけない(かも)
|
692
|
+
elsif within_context?(:caption) # リストや画像のキャプションでも
|
693
|
+
"{\\reviewcode[caption]{#{s}}}" # 背景色をつけない(かも)
|
694
|
+
else # それ以外では
|
695
|
+
"{\\reviewcode{#{s}}}" # 背景色をつける(かも)
|
696
|
+
end
|
697
|
+
}
|
698
|
+
end
|
699
|
+
|
700
|
+
## @<b>{} が //terminal{ ... //} で効くように上書き
|
701
|
+
def inline_b(str)
|
702
|
+
on_inline_b { escape(str) }
|
703
|
+
end
|
704
|
+
def on_inline_b() # nestable
|
705
|
+
if within_codeblock?()
|
706
|
+
#"{\\bfseries #{yield}}" # \seqsplit{} 内では余計な空白が入る
|
707
|
+
#"{\\bfseries{}#{yield}}" # \seqsplit{} 内では後続も太字化する
|
708
|
+
"\\bfseries{}#{yield}\\mdseries{}" # \seqsplit{} 内でうまく効く
|
709
|
+
else
|
710
|
+
macro('reviewbold', yield)
|
711
|
+
end
|
712
|
+
end
|
713
|
+
|
714
|
+
## @<del>{} が //list や //terminal で効くように上書き
|
715
|
+
def inline_del(str)
|
716
|
+
on_inline_del { escape(str) }
|
717
|
+
end
|
718
|
+
def on_inline_del()
|
719
|
+
if within_codeblock?()
|
720
|
+
#"\\reviewstrike{#{yield}}" # \seqsplit{} 内でエラーになる
|
721
|
+
#"{\\reviewstrike{#{yield}}}" # \seqsplit{} 内でもエラーにならないが折り返しされない
|
722
|
+
"{\\reviewstrike{\\seqsplit{#{yield}}}}" # エラーにならないし、折り返しもされる
|
723
|
+
else
|
724
|
+
macro('reviewstrike', yield)
|
725
|
+
end
|
726
|
+
end
|
727
|
+
|
728
|
+
def build_inline_href(url, escaped_label) # compile_href()をベースに改造
|
729
|
+
flag_footnote = @book.config['starter']['linkurl_footnote']
|
730
|
+
return _inline_hyperlink(url, escaped_label, flag_footnote)
|
731
|
+
end
|
732
|
+
|
733
|
+
## @<href>{} の代わり
|
734
|
+
def inline_hlink(str)
|
735
|
+
url, label = str.split(/, /, 2)
|
736
|
+
flag_footnote = @book.config['starter']['hyperlink_footnote']
|
737
|
+
return _inline_hyperlink(url, escape(label), flag_footnote)
|
738
|
+
end
|
739
|
+
|
740
|
+
def _inline_hyperlink(url, escaped_label, flag_footnote)
|
741
|
+
if /\A[a-z]+:/ !~ url
|
742
|
+
"\\ref{#{url}}"
|
743
|
+
elsif ! escaped_label.present?
|
744
|
+
"\\url{#{escape_url(url)}}"
|
745
|
+
elsif ! flag_footnote
|
746
|
+
"\\href{#{escape_url(url)}}{#{escaped_label}}"
|
747
|
+
elsif within_context?(:footnote)
|
748
|
+
"#{escaped_label}(\\url{#{escape_url(url)}})"
|
749
|
+
else
|
750
|
+
"#{escaped_label}\\footnote{\\url{#{escape_url(url)}}}"
|
751
|
+
end
|
752
|
+
end
|
753
|
+
private :_inline_hyperlink
|
754
|
+
|
755
|
+
def build_inline_ruby(escaped_word, escaped_yomi) # compile_ruby()をベースに改造
|
756
|
+
"\\ruby{#{escaped_word}}{#{escaped_yomi}}"
|
757
|
+
end
|
758
|
+
|
759
|
+
def inline_bou(str)
|
760
|
+
## original
|
761
|
+
#str.split(//).map { |c| macro('ruby', escape(c), macro('textgt', BOUTEN)) }.join('\allowbreak')
|
762
|
+
## work well with XeLaTeX as well as upLaTeX
|
763
|
+
str.split(//).map {|c| "\\ruby{#{escape(c)}}{#{BOUTEN}}" }.join('\allowbreak')
|
764
|
+
end
|
765
|
+
|
766
|
+
protected
|
767
|
+
|
768
|
+
## ノートを参照する
|
769
|
+
def build_noteref(chapter, label, caption)
|
770
|
+
"\\starternoteref{#{label}}{#{escape(caption)}}"
|
771
|
+
end
|
772
|
+
|
773
|
+
## 数式を参照する
|
774
|
+
def build_eq(chapter, label, number)
|
775
|
+
#"\\reviewequationref{#{chapter.number}.#{number}}"
|
776
|
+
escape("#{I18n.t('equation')}#{chapter.number}.#{number}")
|
777
|
+
end
|
778
|
+
|
779
|
+
end
|
780
|
+
|
781
|
+
|
782
|
+
end
|